为什么 Promise 无法捕获 setTimeout 抛出的错误?

4

为什么下面的代码无法捕获错误?'catch'不起作用,错误被抛出并退出。我在NodeJs 8.9上运行了这段代码。

new Promise(function(resolve,reject){
    console.log('construct a promise...')
    setTimeout(() => {
        throw new Error('async operation failure!')
    },1000)
})
.then(() => {
    //never happen
    console.log('happen?wrong!!!')
})
.catch(e => {
    console.warn('execute promise failure! catch it')
})

如果去掉setTimeout,'catch' 就会起作用。
new Promise(function(resolve,reject){
    console.log('construct a promise...')
    throw new Error('async operation failure!')
})
.then(() => {
    //never happen
    console.log('happen?wrong!!!')
})
.catch(e => {
    console.warn('execute promise failure! catch it')
})

为什么会发生这种情况?请帮忙,谢谢!

在 Promise 构造函数中,你应该使用 reject 而不是 throw,顺便说一下 - 这与 JavaScript 的执行上下文有关,我想。 - Jaromanda X
1
它就发生在事件循环的另一个轮回中。 - Daniel A. White
2个回答

8
为了理解为什么不能在setTimeout的回调函数中直接抛出错误,您需要了解一些概念。

调用栈

程序运行时将信息存储在称为调用栈的数据结构中。当调用一个方法时,信息存储在堆栈上,直到该方法返回。由于方法通常包含对其他方法的调用,因此堆栈增长并缩小,直到所有方法都返回(通常是程序的结束)。当抛出错误时,它会在尚未返回的函数调用堆栈上传递,直到被捕获和处理,或者到达“main”方法,这通常会导致程序崩溃。

事件循环

事件循环是支持JavaScript中异步功能的核心。当调用setTimeout或任何其他异步方法时,回调会被放置在名为事件循环的队列中。当JavaScript运行时执行程序并到达堆栈的末尾(例如,所有方法都已返回),它不仅退出程序,而是首先查看事件循环中是否有任何内容,如果有,则开始执行它,导致堆栈再次增长和缩小。当事件循环中没有剩余内容,且堆栈为空时,程序就可以自由退出。
这意味着当您调用setTimeout时,传入该方法的回调最终会在不同的堆栈帧中执行。因此,错误无法沿着堆栈抛出并被Promise捕获,因为创建Promise的堆栈已经完成执行并且不存在了。
我建议观看这个主题的视频,它详细介绍了这个过程,并帮助您可视化正在发生的事情: What the heck is the event loop anyway?

谢谢,很有帮助! - Dean Chen

5

setTimeout()有它自己的异步回调。在这个回调中抛出的异常只会返回到定时器事件系统,不会传递到其他地方,因此promise无法捕获它们。你应该从setTimeout()内部调用reject(...)

new Promise(function(resolve,reject){
    console.log('construct a promise...')
    setTimeout(() => {
        reject(new Error('async operation failure!'));
    },1000)
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接