promise.then与promise.catch 链式调用与错误捕获

更新日期: 2022-05-31 阅读: 1.5k 标签: 错误

开讲之前,先做一道题目。如果同时包含多个.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"

你回答对了吗?

原文 https://zhuanlan.zhihu.com/p/522113410

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/11630

相关推荐

解决Cannot read property range of null 错误

vue工程npm run serve/start/dev启动时,node_modules文件报:Cannot read property range of null 错误,该问题是babel-eslint版本更新问题导致的;

开始使用Vue 3时应避免的10个错误

Vue 3 稳定已经有一段时间了。许多代码库正在生产中使用它,其他人最终也必须进行迁移。我有机会与它一起工作,并记录了我的错误,这可能是你想避免的。

HTTP 400 错误 - 请求无效 (Bad request)

在ajax请求后台数据时有时会报 HTTP 400 错误 - 请求无效 (Bad request);出现这个请求无效报错说明请求没有进入到后台服务里;原因:前端提交数据的字段名称或者是字段类型和后台的实体类不一致

nodejs提示 cross-device link not permitted, rename 错误解决方法

文件上传的功能时候,调用fs.renameSync方法错误,这个提示是跨区重命名文件出现的权限问题。先从源文件拷贝到另外分区的目标文件,然后再unlink,就可以了。

Js中使用innerHTML的缺点是什么?

如果在JavaScript中使用innerHTML,缺点是:内容随处可见;不能像“追加到innerHTML”一样使用;innerHTML不提供验证,因此我们可能会在文档中插入有效的和破坏性的HTML并将其中断

javascript如何抛出错误?

throw语句用来抛出一个用户自定义的异常。当前函数的执行将被停止(throw之后的语句将不会执行),并且控制将被传递到调用堆栈中的第一个catch块。如果调用者函数中没有catch块,程序将会终止。

解决typescript Cannot redeclare block-scoped variable

没有依赖框架来写typescript,纯粹新建一个ts文件,使用tsc编译成js后,ts文件里的声明的变量、函数名都会报错:其实我们写的ts代码是没有问题的,只是ts会对我们声明的变量、具名函数、class都放在了全局作用域

不能执行已释放Script的代码

父页面初始化声明变量a为数组(数组对象是引用类型,赋值传递的是地址),创建iframe子页面后给父页面变量a赋值,赋值后销毁iframe子页面,再次调用变量a的时候就会抛出异常‘SCRIPT5011:不能执行已释放Script的代码’。

避免那些可恶的cannot read property of undefined 错误

Uncaught TypeError: Cannot read property foo of undefined. 是一个我们在 JavaScript 开发中都遇到过的可怕错误。或许是某个 API 返回了意料外的空值,又或许是其它什么原因,这个错误是如此的普遍而广泛以至于我们无法判断

自定义错误及扩展错误

当我们在进行开发的时候,通常需要属于我们自己的错误类来反映任务中可能出现的特殊情况。对于网络操作错误,我们需要 HttpError,对于数据库操作错误,我们需要 DbError,对于搜索操作错误,我们需要 NotFoundError,等等

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!