一.关于Promise
promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise
对象。
所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
它代表一个异步操作。有三种状态:
pending
(进行中): 初始状态, 初始状态,未完成或拒绝。
resolved
(已完成): 意味着操作成功完成。又名fulfilled。
rejected
(已失败): 意味着操作失败。
有了Promise
,就可以将异步操作以同步操作的流程表达出,但它也有如下缺点:
一旦创建它就会立即执行,无法中途取消。
如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。
当处于Pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
二.应用示例
- 创建Promise
var promise = new Promise(
/* executor */
function(resolve, reject){
...
}
);
executor函数由Promise实现立即执行,传递resolve和reject函数. (在Promise构造函数之前调用执行器甚至返回创建的对象)
在 executor内部,promise有如下的状态变化可能:
1. resolve被调用,promise由pending变为resolved,代表该Promise被成功解析(resolve)
2.reject被调用,promise由pending变为rejected,代表该Promise的值不能用于后续处理,即被拒绝了
注意:
如果在executor方法的执行过程中抛出了任何异常,那么promise立即被拒绝(即相当于reject方法被调用),executor的返回值也就会被忽略。 如果一个promise对象处在resolved或rejected状态而不是pending状态,那么它也可以被称为settled状态。
2.处理Promise实例
then:Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。
promise.then(function(value) {
// success 状态为Resolved时调用
}, function(error) {
// failure 状态为Reject时调用
});
then方法可以接受两个回调函数作为参数。
第一个回调函数:在promise的状态变成resolved时被调用。它的参数promise的resolve方法的返回值。
第二个回调函数:在promise的状态变成rejected时被调用。它的参数promise的reject方法的返回值。且为可选参数。
catch:Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
promise.then(function(posts) {
// ...
}).catch(function(error) {
console.log('发生错误!', error); // 处理 promise 和 前一个回调函数运行时发生的错误
});
扩展:
Promise.prototype.then
和 Promise.prototype.catch
方法返回 promises对象, 所以它们可以被链式调用。但此时返回的是以函数返回值生成的新的Promise实例,不是原来那个Promise实例。[图片上传中。。。(1)]
注意问题:
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
3.将多个Promise实例,包装成一个新的Promise实例
**Promise.all(iterable)
** :当所有在可迭代参数中的 promises 已完成,或者第一个传递的 promise(指 reject)失败时,返回 promise。
var p = Promise.all([p1, p2, p3]);
当p1、p2和p3的状态均为resolved时p的状态为resolved。
当p1、p2或p3中的任意一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
具体的使用示例如下:
情形一:全部成功的情况
Promise.all([Promise.resolve('foo'),Promise.resolve('bar'),Promise.resolve('test')])
.then(function(result){
console.log(result); //结果为:[foo,bar,test]。即所有返回值的数组。
})
.catch(function(error) {
console.log('error'); //不会被执行
});
情形二:全部失败或部分失败
Promise.all([Promise.resolve('foo'),Promise.reject('barError'),Promise.reject('testError')])
.then(function(result){
console.log(result); //成功回调 不会被执行
})
.catch(function(error) {
console.log(error); //结果为barError或testError。即第一个被reject的值。
});
三.常见的使用误区
忘记添加 .catch()
通常情况,promise有如下两种处理方式:
// ------------ 不好的写法 -------------
promise.then(function(data) {
// success
}, function(err) { //仅处理promise运行时发生的错误。无法处理回调中的错误
// error
});
// ------------ 好的写法 ------------
promise.then(function(data) {
// success
}).catch(function(err) { // 处理 promise 和 前一个回调函数运行时发生的错误
// error
});
因为promise抛出的错误不会传递到外层代码。当使用没有catch的第一种种写法时,成功回调的错误将无法被处理。因此比较好的方式是,总是使用catch方法。
在then或者catch函数中不使用return下面是在是用Promise时,一个很常见的错误写法:
//------------ 不好的写法 ------------------
promise.then(function () {
getUserInfo(userId);
}).then(function () {
// 在这里可能希望在这个回调中使用用户信息,但你可能会发现它根本不存在
});
会发生上面的错误,是因为对Promise的返回及链式调用的理解不够。
每一个promise都会给你一个then()方法(或者catch,它们只是then(null,…)的语法糖)。这里我们是在then()方法的内部来看:
promise.then(function () {
return getUserInfo(userId);
}).then(function (userInfo) {
HadleUser(userInfo);
}).catch(function(error) {
console.log(error);
});
在回调中在有三种事可以做:
返回另一个promise
如果getUserInfo返回一个Promise,则HadleUser将在该promise变为resolved后执行,并以该promise的返回作为入参。
返回一个同步值(或者undefined)
getUserInfo返回一个同步值,则改值会被包装成一个resolved状态的promise,HadleUser
将被立刻执行,并以getUserInfo返回作为入参。
抛出一个同步错误。
getUserInfo返回一个同步错误,则该错误会被包装成一个rejected状态的promise,最终在catch中被捕获。此时HadleUser将不会被执行。
promises丢失
你认为下面的代码会打印出什么?
Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
console.log(result);
});
如果你认为打印出bar,那你就大错特错了。它实际上会打印出foo。
原因是当你给then()传递一个非函数(比如一个promise)值的时候,它实际上会解释为then(null),这会导致之前的promise的结果丢失。
四.最佳实践总结
- then方法中 永远 return或 throw
- 如果 promise链中可能出现错误,一定添加 catch
- 永远传递函数给 then方法
- 不要把 promise写成嵌套
参考链接:
Promise -JavaScript | MDN
Promises 很酷,但很多人并没有理解就在用了
Promise 对象
网友评论