首页 文章

为什么我不能扔进Promise.catch处理程序?

提问于
浏览
101

为什么我不能在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 会被黑洞吞噬?

6 回答

  • 0

    正如其他人所解释的那样,"black hole"是因为扔进一个 .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控制台中的链接将我直接转到发生(原始)错误的文件和行 .

    为什么会这样

    被称为promise履行或拒绝处理程序的函数中的任何异常都会自动转换为拒绝您应该返回的promise . 调用您的函数的promise代码负责这一点 .

    另一方面,setTimeout调用的函数始终从JavaScript稳定状态运行,即它在JavaScript事件循环中以新的循环运行 . 没有任何东西可以捕获异常,并将其发送到Web控制台 . 由于 err 包含有关错误的所有信息,包括原始堆栈,文件和行号,因此仍可正确报告 .

  • 2

    我知道这有点晚了,但我遇到了这个问题,并没有一个解决方案很容易为我实现,所以我提出了自己的:

    我添加了一个小辅助函数,它返回一个promise,如下所示:

    function throw_promise_error (error) {
     return new Promise(function (resolve, reject){
      reject(error)
     })
    }
    

    然后,如果我在我的任何承诺链中有一个特定的位置,我想抛出一个错误(并拒绝承诺),我只是从上面的函数返回我的构造错误,如下所示:

    }).then(function (input) {
     if (input === null) {
      let err = {code: 400, reason: 'input provided is null'}
      return throw_promise_error(err)
     } else {
      return noterrorpromise...
     }
    }).then(...).catch(function (error) {
     res.status(error.code).send(error.reason);
    })
    

    这样我就可以控制从promise链中抛出额外的错误 . 如果你还想处理“正常”的承诺错误,你可以扩展你的catch以分别处理'self-thrown'错误 .

    希望这有帮助,这是我的第一个stackoverflow答案!

  • 40

    Important things to understand here

    • thencatch 函数都返回新的promise对象 .

    • 抛出或明确拒绝,将当前的承诺移动到被拒绝的状态 .

    • 由于 thencatch 返回新的promise对象,因此可以将它们链接起来 .

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

    • 如jfriend00所述, thencatch 处理程序不会同步执行 . 当一个处理程序抛出时,它会立即结束 . 因此,堆栈将被展开,异常将丢失 . 这就是为什么抛出异常会拒绝当前的承诺 .


    在您的情况下,您通过抛出 Error 对象拒绝 do1 内部 . 现在,当前的promise将处于被拒绝状态,控件将被转移到下一个处理程序,在我们的例子中是 then .

    由于 then 处理程序没有拒绝处理程序, do2 根本不会执行 . 您可以在其中使用 console.log 来确认 . 由于当前的promise没有拒绝处理程序,它也会被前一个promise的拒绝值拒绝,控件将被转移到下一个处理程序 catch .

    由于 catch 是拒绝处理程序,当你在其中执行 console.log(err.stack); 时,您可以看到错误堆栈跟踪 . 现在,您正在从中抛出 Error 对象,因此 catch 返回的承诺也将处于拒绝状态 .

    由于您没有将任何拒绝处理程序附加到 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] }
    

    catch 处理程序1中,您将 promise 对象的值视为已拒绝 .

    同样的方式, catch 处理程序1返回的promise也被拒绝,并且 promise 被拒绝的同一错误,我们在第二个 catch 处理程序中观察它 .

  • 0

    我尝试了上面详述的 setTimeout() 方法......

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

    令人讨厌的是,我发现这完全是不可测试的 . 因为's throwing an asynchronous error, you can'将它包装在 try/catch 语句中,因为 catch 将在时间错误被抛出时停止监听 .

    我只是使用了一个监听器这是完美的,因为它是如何使用JavaScript,是高度可测试的 .

    return new Promise((resolve, reject) => {
        reject("err");
    }).catch(err => {
        this.emit("uncaughtException", err);
    
        /* Throw so the promise is still rejected for testing */
        throw err;
    });
    
  • 134

    the spec (see 3.III.d)

    d . 如果调用然后抛出异常e,a . 如果已调用resolvePromise或rejectPromise,请忽略它 . 湾否则,以e为理由拒绝承诺 .

    这意味着如果您在 then 函数中抛出异常,它将被捕获并且您的承诺将被拒绝 . catch 这里没有意义,它只是 .then(null, function() {}) 的快捷方式

    我想你想在你的代码中记录未处理的拒绝 . 大多数承诺图书馆为它发射了一个 unhandledRejection . 这是relevant gist讨论它 .

  • 3

    是承诺吞下错误,你只能用 .catch grab 它们,正如其他答案中详细解释的那样 . 如果您在Node.js中并且想要重现正常的 throw 行为,将堆栈跟踪打印到控制台并退出进程,则可以执行

    ...
      throw new Error('My error message');
    })
    .catch(function (err) {
      console.error(err.stack);
      process.exit(0);
    });
    

相关问题