为什么我不能放入 Promise.catch 处理程序?把戏为什么有效

为什么我不能在 catch 回调内部抛出一个,让进程像在任何其他作用域中一样处理错误?Error

如果我什么都不做,就会被打印出来,我对发生的事情一无所知。这个过程就结束了...console.log(err)

例:

function do1() {
    return new Promise(function(resolve, reject) {
        throw new Error('do1');
        setTimeout(resolve, 1000)
    });
}

function do2() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            reject(new Error('do2'));
        }, 1000)
    });
}

do1().then(do2).catch(function(err) {
    //console.log(err.stack); // This is the only way to see the stack
    throw err; // This does nothing
});

如果回调在主线程中执行,为什么会被黑洞吞噬?Error


答案 1

正如其他人所解释的那样,“黑洞”是因为在承诺被拒绝的情况下,继续进入链条,而你没有更多的捕获,导致一个未终止的链条,吞噬错误(糟糕!.catch

再添加一个捕获以查看发生了什么:

do1().then(do2).catch(function(err) {
    //console.log(err.stack); // This is the only way to see the stack
    throw err; // Where does this go?
}).catch(function(err) {
    console.log(err.stack); // It goes here!
});

当您希望链在步骤失败的情况下继续运行时,链中间的捕获很有用,但是重新抛掷对于在执行诸如记录信息或清理步骤之类的操作后继续失败很有用,甚至可能更改引发的错误。

把戏

为了使错误在Web控制台中显示为错误,正如您最初打算的那样,我使用以下技巧:

.catch(function(err) { setTimeout(function() { throw err; }); });

即使行号仍然存在,因此Web控制台中的链接将我直接带到发生(原始)错误的文件和行。

为什么有效

称为承诺履行或拒绝处理程序的函数中的任何异常都会自动转换为拒绝您应该返回的承诺。调用函数的承诺代码负责处理此问题。

另一方面,setTimeout调用的函数总是从JavaScript稳定状态运行,即它在JavaScript事件循环的新周期中运行。异常不会被任何东西捕获,并进入Web控制台。由于包含有关错误的所有信息,包括原始堆栈,文件和行号,因此仍然可以正确报告。err


答案 2

这里要了解的重要事项

  1. 和 函数都返回新的 promise 对象。thencatch

  2. 无论是抛出还是明确拒绝,都会将当前的承诺移动到被拒绝的状态。

  3. 由于返回新的 promise 对象,它们可以被链接起来。thencatch

  4. 如果在 promise 处理程序 ( or ) 中抛出或拒绝,它将在链接路径向下的下一个拒绝处理程序中处理。thencatch

  5. 正如 jfriend00 所提到的,和 处理程序不是同步执行的。当处理程序抛出时,它将立即结束。因此,堆栈将被解卷,异常将丢失。这就是为什么抛出异常会拒绝当前的承诺。thencatch


在你的情况下,你通过抛出一个物体来拒绝内部。现在,当前承诺将处于拒绝状态,控制权将转移到下一个处理程序,这就是我们的情况。do1Errorthen

由于处理程序没有拒绝处理程序,因此根本不会执行 。您可以通过在内部使用来确认这一点。由于当前 promise 没有拒绝处理程序,因此它也将使用上一个 promise 中的拒绝值被拒绝,并且控制权将转移到下一个处理程序,即 。thendo2console.logcatch

与拒绝处理程序一样,当您在其中执行此操作时,您可以看到错误堆栈跟踪。现在,您正在从中抛出一个对象,因此 返回的 promise 也将处于拒绝状态。catchconsole.log(err.stack);Errorcatch

由于您尚未将任何拒绝处理程序附加到 ,因此无法观察到拒绝。catch


你可以拆分链条并更好地理解这一点,就像这样

var promise = do1().then(do2);

var promise1 = promise.catch(function (err) {
    console.log("Promise", promise);
    throw err;
});

promise1.catch(function (err) {
    console.log("Promise1", promise1);
});

您将获得的输出将类似于

Promise Promise { <rejected> [Error: do1] }
Promise1 Promise { <rejected> [Error: do1] }

在处理程序 1 中,您将获得对象的值为已拒绝。catchpromise

同样,处理程序 1 返回的 promise 也会被拒绝,并带有与被拒绝相同的错误,我们正在第二个处理程序中观察到它。catchpromisecatch