由面试题Promise引发的错误异常处理的探索

119 阅读3分钟

问:如何处理Promise抛出的错误异常 答:使用catch方法以链式调用的方式进行捕获

问:async/await的异常处理呢? 答:使用try...catch

问:那Promise可以使用try...catch捕获到吗? 答:不可以

问:那为什么Promise不可以try...catch,但是async/await可以呢? 答:async/await本质也是基于promise和generator实现的语法糖,具体原理机制不清楚,猜测是async/await内部throw了error,才能通过try-catch捕获到吧。

面试官:emm,自己回去可以看看。。。

以上面试对话,最后一个问题,我也没有太深的研究过。面试后找了些资料,这里记录下 我觉得自己答的确实没有到点上,原因是当时没有认识到以下的知识点:

首先,要明白一点,异常错误是由层级的,比如在某个函数内出现的异常,如果你没有catch到,那么错误会一直沿着调用栈往上一层throw,如果上一层也没有catch到,那么继续往上报错,最终到达全局,变成了全局错误。

第二点,错误会引发什么问题?会导致程序以错误终止,导致程序的崩溃。也就是说你的代码程序到此为止了,后面的代码也不会再执行了,devtool会出现红色错误提示。

第三点,错误还有很多类型,promise只是其中一种,这里不发散开了, 重点讨论promise类型的。这一块可以去jartto.wang/2018/11/20/… 了解或者网上查下,比较容易找得到。

错误到导致程序崩溃,不再运行,比较常见,相信基本都遇到过。 捕获到异常的神奇之处就在于:能让程序继续执行,而不会中断。

// 执行流:
new Promise((resolve, reject) => {

  throw new Error("Whoops!");

}).catch(function(error) {

  alert("The error is handled, continue normally");

}).then(() => alert("Next successful handler runs"));

上面代码promise抛出的错误被catch住了,所以继续执行了then中的回调函数。如果去掉了catch, 那么then中的函数也不会继续执行了。自己试了,promise中的错误,会阻塞then中的执行,但是不会影响promise外面的其他代码。

image.png 上面讲了关于异常的机制和处理方式,下面重点看promise的错误处理。

隐式的try...catch

promise 的执行者(executor)和 promise 的处理程序周围有一个隐式的 try..catch。如果发生异常,它就会被捕获,并被视为 rejection 进行处理。

new Promise((resolve, reject) => { 
    throw new Error("Whoops!");
}).catch(alert);
// Error: Whoops!

等同于:

new Promise((resolve, reject) => {
  reject(new Error("Whoops!"));
}).catch(alert); // Error: Whoops!

在 executor 周围的“隐式 try..catch”自动捕获了 error,并将其变为 rejected promise。JavaScript 引擎会跟踪此类 rejection,在这种情况下会生成一个全局的 error。如果你运行上面这个代码,你可以在控制台中看到。

在浏览器中,我们可以使用 unhandledrejection 事件来捕获这类 error:

window.addEventListener('unhandledrejection', function(event) {
  // 这个事件对象有两个特殊的属性:
  alert(event.promise); // [object Promise] —— 生成该全局 error 的 promise
  alert(event.reason); // Error: Whoops! —— 未处理的 error 对象
});

new Promise(function() {
  throw new Error("Whoops!");
}); // 没有用来处理 error 的 catch

补充:catch无法捕获异步的error

new Promise(function(resolve, reject) {
  setTimeout(() => {
    throw new Error("Whoops!");
  }, 1000);
}).catch(alert);

image.png 一秒后,错误爆出了。我的理解是:setTimeout回调中的函数是全局作用域执行,在当前promise执行栈中看不到,因为已经上升到全局作用域了。应该用window.onunhandledrejection来监听处理

window.addEventListener('unhandledrejection', (e) => {
    console.log(e, 'catch you')
});
new Promise((res, rej) => {
    setTimeout(() => {
        rej("Whoops!");
    }, 1000)
})

有不对之处,欢迎大神指正。 下回继续更新