实现Promise.prototype.catch
catch
方法的实现非常简单,它的作用用于捕获一段Promise程序的异常,通常我们会将它放到Promise调用链的最下方。之所以说它非常简单因为这个作用已经被then
方法实现了,所以我们只需要对then
方法进行下包装即可:
MyPromise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
如果您没有理解上面的实现,可以返回到上面构造函数和then
方法实现中仔细查看一下回调函数的调用过程以及出现异常时的应对方法。关于catch
方法的调用机制,我们以以下规则进行处理异常:
- 如果Promise的执行状态为
rejected
,那么并不会直接决定接下来的Promise是否为rejected
,而是同resolved
状态一样,根据函数的执行结果决定。这条非常重要,我在这里再次重复一遍; - 我们在
then
方法中对传入的onRejected
回调方法进行了判断,如果其不为一个函数,我们会规定它的默认行为为直接抛出拿到的拒因,这也是为什么Promise的拒因可以一直穿透到最下方的catch
方法中的原因。但是,正如我们上一条规则所提到的,如果您一旦指定了onRejected
,下方的then
方法就会根据传入的回调函数的执行结果来决定执行状态。简单得说就是,如果在catch
方法的上方,有其它onRejected
对拿到的拒因进行了处理,并且该过程并没有发生或者主动抛出异常,最下方的catch
是不会拿到拒因的。
我的文字描述可能不能直接让您理解这个理念,您可以结合下方这段代码来进行理解:
let asyncCode = new Promise((resolve, reject) => {
setTimeout(() => {
// 这里的失败态会调用第一个then方法中的onRejected()
reject(1);
});
});
asyncCode.then(
(value) => {
// 不会执行
console.log(e);
},
(reason) => {
// 我们覆盖了onRejected的默认行为,但是我们主动抛出了异常
throw reason;
}
).then(
(value) => {
// 这里的代码同样不会执行
},
(reason) => { // *
// 我们在这里对拿到的拒因进行了操作,并且这段代码不会抛出异常(尽管它看起来很蠢)
console.log(reason + "2"); // 输出:"12"
}
).catch((e) => {
// 所以这里的代码是不会执行的,因为上面的onRejected()执行结果(返回值)为undefined
console.log(e + 1);
});
但是如果将标记*
的失败处理函数删掉或者在其中使用throw
主动抛出了错误,又或者是该函数中的逻辑代码出现了异常,下方的catch
就会被执行。