开讲之前,先做一道题目。如果同时包含多个.then和.catch,且.then包含第二个用与错误处理的函数,运行结果什么?
// code 1 new Promise((resolve, reject) => { reject('error') }).then(()=> { console.log('ok 1') }, (err) => { console.log('error 1: ' + err) }).then(() => { console.log('ok 2') }, (err) => { console.log('error 2: ' + err) }).catch(err => { console.log('catch 1: ' + err) })
给出以下4个备选答案,你会选什么?
答案 A error 1: error error 2: error catch 1: error 答案 B error 1: error 答案 C error 1: error ok 2 答案 D error 1: error ok 2 catch 1: error
往下滚动揭晓答案
正确答案是 C。
code1 等价于以下写法
// code 2 let p1 = new Promise((resolve, reject) => { reject('error') }) let p2 = p1.then(()=> { //line 4 console.log('ok 1') }, function fn2(err) { console.log('error 1: ' + err) }) let p3 = p2.then(() => { //line 9 console.log('ok 2') }, function fn3(err) { console.log('error 2: ' + err) }) let p4 = p3.catch(function fn4(err) { //line 14 console.log('catch 1: ' + err) }) console.log(p1, p2, p3, p4)
首先执行同步的代码,创建已经 rejected 的p1 和处于 pending的 p2、p3、p4。
p1的reject 触发 p1.then的第二个参数,也就是fn2的运行, 输出 error 1: error
。fn2运行后此时p2变成 fulfilled 状态 (注意不是rejected,可以理解为函数的执行意味着错误的处理,错误处理了就圆满了,后面就没错误了),同时触发p2.then的第一个参数的运行(line 9),输出 ok 2
。此刻p3变成fulfilled,p3.catch不会捕捉到任何错误,fn4不会运行。
如果fn2不存在呢?如code3 所示,p1.then的第二个参数没有传递
// code 3 let p1 = new Promise((resolve, reject) => { reject('error') }) let p2 = p1.then(()=> { //line 4 console.log('ok 1') }) //注意这里.then没第二个函数参数 let p3 = p2.then(() => { //line 7 console.log('ok 2') }, function fn3(err) { // line9 console.log('error 2: ' + err) }) let p4 = p3.catch(function fn4(err) { //line 12 console.log('catch 1: ' + err) }) console.log(p1, p2, p3, p4)
以上代码的输出是
error 2: error
根据 Promise A+规范 2.2.7.3, 对于 p2 = p1.then(onFulfilled, onRejected)
,如果onRejected
不是一个函数,并且 p1 被 rejected, p2 一定被 rejected 并且使用和 p1 相同的 reason。
Promise A+规范
因为代码中 p1.then的第二个参数为空,所以 p1的reject 触发p2 变为rejected。同时触发p2.then的第二个参数fn3的运行(line 9),输出 error2: error
。此刻p3变成fulfilled,p3.catch不会捕捉到任何错误,fn4不会运行。
假设去掉line9 里的fn3呢?
// code 4 let p1 = new Promise((resolve, reject) => { reject('error') }) let p2 = p1.then(()=> { //line 4 console.log('ok 1') }) //注意这里.then没第二个函数参数 let p3 = p2.then(() => { //line 7 console.log('ok 2') }) //注意这里.then没第二个函数参数 let p4 = p3.catch(function fn4(err) { //line 12 console.log('catch 1: ' + err) }) console.log(p1, p2, p3, p4)
以上代码输出结果是,分析过程同上。
catch 1: error
我们也可以在中途 throw 错误
// code 5 let p1 = new Promise((resolve, reject) => { reject('error') }) let p2 = p1.then(()=> { //line 4 console.log('ok 1') }) //注意这里.then没第二个函数参数 let p3 = p2.then(() => { //line 7 console.log('ok 2') }, function fn3(err) { // line9 console.log('error 2: ' + err) throw 'error 3' // line11 注意这里 throw了错误,p3会变成rejected状态,错误会交给后续处理 }) let p4 = p3.catch(function fn4(err) { //line 13 console.log('catch 1: ' + err) }) console.log(p1, p2, p3, p4)
如code5 的 line 11所示, 如果没加 throw 'error 3'
这行代码,p3内处理了错误,p3变成fulfilled 状态,不会触发 p3.catch里的 fn4 的执行 (line 13)。 如果 加了 throw 'error 3'
,则又抛出一个错误,p3变为rejected 状态,会触发 p3.catch里的 fn4 的执行。
code5 代码输出为
error2: error catch1: error3
错误在传递的过程中如果被中途“处理”(触发then的第二个函数参数,或者被catch),则不再传递。否则继续传递,直到被处理。错误被"处理"之后,过程中的Promise对象全部都是完成状态的。
试一试如下代码
//code 6 new Promise((resolve, reject) => { reject('error') //2 }).then(()=> { console.log('ok 1') //4 }, (err) => { console.log('error 1:' + err) //6 }).then(() => { console.log('ok 2') //8 }, (err) => { console.log('error 2:' + err) //10 }).then(() => { console.log('ok 3') //12 }, (err) => { console.log('error 3:' + err) //14 }).catch(err => { console.log('catch 1:' + err) //16 }).finally(() => { console.log('finally 1') //18 }).then(() => { console.log('ok 4') //19 }, (err) => { console.log('error 4:' + err) //20 }).finally(() => { console.log('finally 2') //22 }).catch(err => { console.log('catch 2:' + err) //24 })
以上代码运行结果是
"error 1:error" "ok 2" "ok 3" "finally 1" "ok 4" "finally 2"
你回答对了吗?
最后推荐一下我的 Promise精讲课程 Promise 精讲(大厂/15K以上前端求职面试必考知识点) 。
推荐小课的大纲
饥人谷一直致力于培养有灵魂的编程者,打造专业有爱的国内前端技术圈子。如造梦师一般帮助近千名不甘寂寞的追梦人把编程梦变为现实,他们以饥人谷为起点,足迹遍布包括facebook、阿里巴巴、百度、网易、京东、今日头条、大众美团、饿了么、ofo在内的国内外大小企业。 了解培训课程:加微信 xiedaimala03,官网:https://jirengu.com