链式调用
Promise的.then方法会返回一个新的Promise(catch同理),所以可以将then链接起来。
它的值和状态都取决于上一个then的执行情况
1、上一个then没有返回值
新promise实例状态为fulfilled,值为undefined
let p1 = new Promise((resolve,reject) => {
resolve(1);
})
let p2 = p1.then((value) => {
console.log('xxx');
})
// p1完成后,p2:{<fulfilled>: undefined}
2、上一个then返回非promise的任意值
新promise实例状态为fulfilled,值为返回值
let p1 = new Promise((resolve,reject) => {
resolve(2);
})
let p2 = p1.then((value) => {
return 6;
})
// p1完成后,p2:{<fulfilled>: 6}
3、上一个then显式地返回一个promise
新promise实例的结果等于返回的promise的结果
let p1 = new Promise((resolve,reject) => {
resolve(3);
})
let p2 = p1.then((value) => {
return Promise.resolve(9);
})
// p1完成后,p2:{<fulfilled>: 9}
4、上一个then没有提供onResolved或者onRejected方法
如果上一个promise实例是成功的,而then没有提供onResolved方法,就基于上一次resolve的结果
let p1 = new Promise((resolve,reject) => {
resolve(2);
})
let p2 = p1.then();
// p1完成后,p2:{<fulfilled>: 2}
5、上一个then中抛出错误
新promise实例状态为rejected,值为抛出的异常
let p1 = new Promise((resolve,reject) => {
resolve(2)
})
let p2 = p1.then((value) => {
throw new Error('fail');
})
// p1完成后,p2:{<rejected>: 'fail'}
中断promise链
如果在某一步后,不想再执行后面的then,从此中断promise链,可以返回一个pending状态的promise:
new Promise((resolve, reject) => {
resolve(100)
}).then(value => {
}).then(value => {
//到此终止
return new Promise(() => {})
}).then(value => {
}).catch(reason => {
console.log(reason)
})
这样写的话,最后一个then和最后一个catch都不会执行(因为无法接收到 fulfilled 或者 rejected 的promise实例)
错误处理
前面已经说过了,Promise中的错误处理,拒绝处理函数会被传递到then函数的第二个参数,或者用catch来捕捉。如果then中的回调函数执行出错,那么这个then的返回一个rejected状态的Promise,如:
var p = Promise.resolve(42);
p.then((msg) => {
console.log(msg.toLowerCase());
}, (err) => {
console.log(err); // 永远不会执行
})
p的状态是fulfilled,返回值42,但是在起成功处理函数中,number类型没有toLowerCase方法,所以会抛出一个错误。
但为什么这个错误不会被我们的错误处理函数捕获呢?
因为p这个promise已经用值42填充了,变成了fulfiiled状态,它不会再被改变。所以p.then(...)里面的错误,会被通知到p.then(...).then(...)中,但是我们没有在这里捕捉。
为了避免丢失被忽略或抛弃的Promise错误,一些开发者表示,最佳实践是在最后总以一个catch(..)结束,这种处理叫异常穿透。
如果第一个开头的promise失败了,但是后面的then都没有写onRejected函数:
new Promise((resolve, reject) => {
reject(1);
}).then(value => {
}).then(value => {
}).then(value => {
}).catch(reason => {
console.log(reason);
})
那么这个reject(1)就会一层一层透传到最后一个catch(注意:它不是一下子找到最后一个catch的)
无论是then还是catch,它的执行情况都取决于上一个then()
因为then不手动写onRejected函数,默认被这样处理:
.then(value => {
}, reason => {
throw reason;
})
但是不管怎样,在Promise链的最后一步,总存在着未捕获的错误的可能性,尽管这种可能性越来越低。
有没有办法解决这个问题呢?
有一些Promise库增加了一些方法,用于注册一个类似“全局未处理拒绝”的处理函数,这样就不会抛出全局错误,而是调用这个函数。
window.addEventListener("unhandledrejection", handler);
有一种看法是:Promise应该添加一个done函数,从本质上标识标识Promise链的结束。done的回调中抛出的错误,会被当作一个全局未处理的错误,可以在try...catch块中捕获到。
然而,它并不是ES6标准的一部分,我们可以选择自己实现它:
Promise.prototype.done = function (onFulfilled, onRejected) {
this
.then(onFulfilled, onRejected)
.catch(function (reason) {
// 抛出一个全局错误
setTimeout(() => {
throw reason
}, 0);
})
}