JavaScript Promise 用法


Posted in Javascript onJune 14, 2016

同步编程通常来说易于调试和维护,然而,异步编程通常能获得更好的性能和更大的灵活性。异步的最大特点是无需等待。“Promises”渐渐成为JavaScript里最重要的一部分,大量的新API都开始promise原理实现。下面让我们看一下什么是promise,以及它的API和用法!

Promises现状

XMLHttpRequest API是异步的,但它没有使用promise API。但有很多原生的 javascript API 使用了promise:

*Battery API
*fetch API (XHR的替代品)
*ServiceWorker API

Promises将来只会变得越来越流行、普遍,非常重要,所有的前端开发人员都将用到它。另一个值得注意的是,Node.js是基于Promises的平台(很显然,Promise是它的一个核心特征)。

Promises的用法比你想象的要简单——如果你以前喜欢使用setTimeout来控制异步任务的话!

Promise基本用法

new Promise()构造器可以用在传统的异步任务中,就像以前 setTimeout 和 XMLHttpRequest 的用法一样。一个新的 Promise 使用 new 关键字生成,同时,这个 Promises 提供了 resolve 和 reject 函数让我们执行回调操作:

var p = new Promise(function(resolve, reject) {
 
 // Do an async task async task and then...

 if(/* good condition */) {
 resolve('Success!');
 }
 else {
 reject('Failure!');
 }
});

p.then(function() { 
 /* do something with the result */
}).catch(function() {
 /* error */
})

程序员可以手动的在回调函数内部根据执行情况调用 resolve 和 reject 函数。下面是一个比较具有现实意义的例子,它将一个 XMLHttpRequest 调用转换为 基于 Promises 的任务:

// From Jake Archibald's Promises and Back:
// http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest

function get(url) {
 // Return a new promise.
 return new Promise(function(resolve, reject) {
  // Do the usual XHR stuff
  var req = new XMLHttpRequest();
  req.open('GET', url);

  req.onload = function() {
   // This is called even on 404 etc
   // so check the status
   if (req.status == 200) {
    // Resolve the promise with the response text
    resolve(req.response);
   }
   else {
    // Otherwise reject with the status text
    // which will hopefully be a meaningful error
    reject(Error(req.statusText));
   }
  };

  // Handle network errors
  req.onerror = function() {
   reject(Error("Network Error"));
  };

  // Make the request
  req.send();
 });
}

// Use it!
get('story.json').then(function(response) {
 console.log("Success!", response);
}, function(error) {
 console.error("Failed!", error);
});

Promise.resolve() 和 Promise.reject() 可以直接被调用。有时候,当判断出 promise 并不需要真正执行时,我们并不需要 使用 new 创建 Promise 对象,而是可以直接调用 Promise.resolve() 和 Promise.reject()。比如:

var userCache = {};

function getUserDetail(username) {
 // In both cases, cached or not, a promise will be returned

 if (userCache[username]) {
  // Return a promise without the "new" keyword
  return Promise.resolve(userCache[username]);
 }

 // Use the fetch API to get the information
 // fetch returns a promise
 return fetch('users/' + username + '.json')
  .then(function(result) {
   userCache[username] = result;
   return result;
  })
  .catch(function() {
   throw new Error('Could not find user: ' + username);
  });
}

因为 promise 肯定会返回,所以,我们可以使用 then 和 catch 方法处理返回值!

then 方法

所有的 promise 对象实例里都有一个 then 方法,它是用来跟这个 promise 进行交互的。首先,then 方法会缺省调用 resolve() 函数:

new Promise(function(resolve, reject) {
 // A mock async action using setTimeout
 setTimeout(function() { resolve(10); }, 3000);
})
.then(function(result) {
 console.log(result);
});

// From the console:
// 10

then 回调动作的触发时机是 promise 被执行完。我们还可以串联 then 方法执行回调操作:

new Promise(function(resolve, reject) { 
 // A mock async action using setTimeout
 setTimeout(function() { resolve(10); }, 3000);
})
.then(function(num) { console.log('first then: ', num); return num * 2; })
.then(function(num) { console.log('second then: ', num); return num * 2; })
.then(function(num) { console.log('last then: ', num);});

// From the console:
// first then: 10
// second then: 20
// last then: 40

你会发现,每次 then 调用都会以之前的 then 调用的返回值为参数。

如果一个 promise 已经执行完成,单 then 被再次调用时,回调动作将会被再次执行。而如果这个 promise 里执行的是reject 回调函数,这是再调用 then 方法,回调函数将不会被执行。

catch 方法

catch 当一个 promise 被拒绝(reject)时,catch 方法会被执行:

new Promise(function(resolve, reject) {
 // A mock async action using setTimeout
 setTimeout(function() { reject('Done!'); }, 3000);
})
.then(function(e) { console.log('done', e); })
.catch(function(e) { console.log('catch: ', e); });

// From the console:
// 'catch: Done!'

通常我们在 reject 方法里处理执行失败的结果,而在catch 里执行异常结果:

reject(Error('Data could not be found'));

Promise.all 方法

在我们的异步调用时经常有这样一种场景:我们需要同时调用多个异步操作,但希望只有等所有的操作都完成后,我们才去执行响应操作——这就是 Promise.all 的作用。 Promise.all 方法可以接收多个 promise 作为参数,以数组的形式,当这些 promise 都成功执行完成后才调用回调函数。

Promise.all([promise1, promise2]).then(function(results) {
 // Both promises resolved
})
.catch(function(error) {
 // One or more promises was rejected
});

一个很好的能演示 Promise.all 用法的例子是,执行多个 AJAX 操作(通过 fetch) 调用:

var request1 = fetch('/users.json');
var request2 = fetch('/articles.json');

Promise.all([request1, request2]).then(function(results) {
 // Both promises done!
});

我们还可将fetch和电池状态API混合一起执行,因为它们返回的都是 promise:

Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) {
 // Both promises done!
});

一旦 promise 里调用了reject函数,也就是执行被拒绝了,没有能够正常完成,情况会有些复杂。一旦 promise 被拒绝,catch 方法会捕捉到首个被执行的reject函数:

var req1 = new Promise(function(resolve, reject) { 
 // A mock async action using setTimeout
 setTimeout(function() { resolve('First!'); }, 4000);
});
var req2 = new Promise(function(resolve, reject) { 
 // A mock async action using setTimeout
 setTimeout(function() { reject('Second!'); }, 3000);
});
Promise.all([req1, req2]).then(function(results) {
 console.log('Then: ', one);
}).catch(function(err) {
 console.log('Catch: ', err);
});

// From the console:
// Catch: Second!

Promise.all 是非常重要的接口,将会在很多新诞生的 promise API中扮演重要的作用。

Promise.race

Promise.race 是一个有趣的函数——它不是等待所有的 promise 被resolve 或 reject,而是在所有的 promise 中只要有一个执行结束,它就会触发:

var req1 = new Promise(function(resolve, reject) { 
 // A mock async action using setTimeout
 setTimeout(function() { resolve('First!'); }, 8000);
});
var req2 = new Promise(function(resolve, reject) { 
 // A mock async action using setTimeout
 setTimeout(function() { resolve('Second!'); }, 3000);
});
Promise.race([req1, req2]).then(function(one) {
 console.log('Then: ', one);
}).catch(function(one, two) {
 console.log('Catch: ', one);
});

// From the console:
// Then: Second!

一个有用的场景是,从多个镜像服务器下载资源,一旦有一个返回,其它的返回也就不用处理了。

学会使用 Promises

Promises在过去几年是一个非常火爆的话题,它甚至从JavaScript里抽离出来变成了一个语言架构。相信很快我们将见到有愈来愈多的JavaScript API将使用以promise为基础的模式。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery点击tr实现checkbox选中的方法
Mar 19 Javascript
JS判断两个时间大小的示例代码
Jan 28 Javascript
jquery选择器原理介绍($()使用方法)
Mar 25 Javascript
jquery 自定义容器下雨效果可将下雨图标改为其他
Apr 23 Javascript
JavaScript使用Math.Min返回两个数中较小数的方法
Apr 06 Javascript
js根据手机客户端浏览器类型,判断跳转官网/手机网站多个实例代码
Apr 30 Javascript
js的三种继承方式详解
Jan 21 Javascript
Angularjs 实现移动端在线测评效果(推荐)
Apr 05 Javascript
详解vue.js的devtools安装
May 26 Javascript
浅谈react-router HashRouter和BrowserRouter的使用
Dec 29 Javascript
javascript变量提升和闭包理解
Mar 12 Javascript
Vue iview-admin框架二级菜单改为三级菜单的方法
Jul 03 Javascript
Javascript基础_简单比较undefined和null 值
Jun 14 #Javascript
Javascript缓存API
Jun 14 #Javascript
JS修改地址栏参数实例代码
Jun 14 #Javascript
JS获取地址栏参数的两种方法(简单实用)
Jun 14 #Javascript
Javascript基础_嵌入图像的简单实现
Jun 14 #Javascript
jQuery实例—选项卡的简单实现(js源码和jQuery)
Jun 14 #Javascript
浅析JS获取url中的参数实例代码
Jun 14 #Javascript
You might like
分页显示Oracle数据库记录的类之一
2006/10/09 PHP
PHP中去除换行解决办法小结(PHP_EOL)
2011/11/27 PHP
CI操作cookie的方法分析(基于helper类库)
2016/03/28 PHP
JavaScript库 开发规则
2009/01/31 Javascript
jquery 判断滚动条到达了底部和顶端的方法
2014/04/02 Javascript
JS实现超简洁网页title标题跑动闪烁提示效果代码
2015/10/23 Javascript
5个最顶级jQuery图表类库插件【jquery插件库】
2016/05/05 Javascript
JS操作JSON方法总结(推荐)
2016/06/14 Javascript
jQuery中JSONP的两种实现方式详解
2016/09/26 Javascript
基于JavaScript实现复选框的全选和取消全选
2017/02/09 Javascript
C#微信小程序服务端获取用户解密信息实例代码
2017/03/10 Javascript
easyui-datagrid特殊字符不能显示的处理方法
2017/04/12 Javascript
Node+Express+MongoDB实现登录注册功能实例
2017/04/23 Javascript
微信小程序实现的涂鸦功能示例【附源码下载】
2018/01/12 Javascript
详解Vue快速零配置的打包工具——parcel
2018/01/16 Javascript
浅谈redux以及react-redux简单实现
2018/08/28 Javascript
Vue实现多标签选择器
2019/11/28 Javascript
vue 数据操作相关总结
2020/12/17 Vue.js
python实现两个文件合并功能
2018/04/01 Python
tensorflow学习笔记之简单的神经网络训练和测试
2018/04/15 Python
sublime python3 输入换行不结束的方法
2018/04/19 Python
pandas 对series和dataframe进行排序的实例
2018/06/09 Python
使用PyQtGraph绘制精美的股票行情K线图的示例代码
2019/03/14 Python
python读取并定位excel数据坐标系详解
2019/06/26 Python
Python matplotlib生成图片背景透明的示例代码
2019/08/30 Python
Python并发concurrent.futures和asyncio实例
2020/05/04 Python
Pycharm学生免费专业版安装教程的方法步骤
2020/09/24 Python
Python 解析xml文件的示例
2020/09/29 Python
python 如何使用find和find_all爬虫、找文本的实现
2020/10/16 Python
新西兰最大的在线设计师眼镜店:SmartBuyGlasses新西兰
2017/10/20 全球购物
Black Halo官方网站:购买连衣裙、礼服和连体裤
2018/06/13 全球购物
澳大利亚珠宝商:Shiels
2019/10/06 全球购物
中国梦我的梦演讲稿
2014/04/23 职场文书
硕士生找工作求职信
2014/07/05 职场文书
退休劳动合同怎么写?
2019/10/25 职场文书
Flask使用SQLAlchemy实现持久化数据
2021/07/16 Python