Promise的异常捕获
本文暂时主要讨论try/catch、promise.catch以及then方法的第二个参数
1.尝试使用try/catch捕获
上述代码中try当中,码块执行过程中遇到的的错误并没有被catch捕获到,这是因为try/catch只能捕获同步的错误,对于异步任务当中出现的错误显得无能为力。
那么什么是同步的错误,什么又是异步错误呢?
2.同步错误和异步错误
当一个事件循环内,同一个任务队列当中出现错误,对于此任务所存在的代码上下文而言,它是同步错误。
这里不得不提到JS的事件循环:
在一次事件循环中,JS会把整个script代码当作宏任务执行执行,执行完成之后再检测本次循环中是否存在微任务,存在的话就一次读取执行微任务的任务队列。宏任务-微任务-宏任务-微任务......如此不断切换的循环下去。
而上文1.代码块中提到的setTimeout和Promise被称为任务源,来自不同的任务源注册的回调函数会被放进不同的任务队列。setTimeout是宿主浏览器发起的任务,一般会被会被放入宏任务;Promise是由JS引擎发起的任务,会被放进微任务。
由此再去看setTimeout中抛出的错误,它已经不在try/catch所在的事件循环了,所以是一个异步错误,无法被捕获到;而Promise.reject虽然是同步执行的,但是reject当中的内容却在另一个微任务循环中,对于try/catch来讲也是异步的,所以二者的错误都无法被try/catch捕捉到。
3.尝试对try代码块中的Promise结果进行改造
对于Promise.reject来讲,即使我们在try当中将其return,也依然不会被当成异常捕获,因为reject会把其内部的错误包装成一个promise对象返回,对象不是异常,所以没有被捕获。
4.async/await异步转同步
通过以上分析,想要使用try/catch对promise内部的异常进行捕获,只有想办法把它转成同步任务。
一般情况下,同步错误如果没有进行捕获,那么整个错误所在的事件循环都将终止,所以在开发阶段没有处理到的错误,使用函数进行兜底是很重要的。
2.使用then的第二个参数和catch进行异常捕获
我们首先要区分几个概念:
-
reject是用来抛出异常的,属于Promise方法。
-
catch是用来处理异常的,属于Promise实例的方法。
-
then和catch是Promise实例的方法(Promise.prototype.then 和 Promise.prototype.catch)。
1.示例代码
2.区别与联系
-
- then的第二个参数和catch捕获错误信息的时候会遵循就近原则。如果是promise内部报错,reject抛出错误后,then的第二个参数和catch方法都存在的情况下,只有then的第二个参数能捕获到,如果then的第二个参数不存在,则catch方法会捕获到。
- 如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到。
- 推荐使用catch进行异常捕获,能兼顾到各种情况,也更接近同步异常的捕获写法(try/catch)。