首页  >  问答  >  正文

始终捕获 Promise 中的错误的重要性

我在我的项目中使用 @typescript-eslint/no-floating-promises 规则。如果我写这样的代码,这条规则就会抱怨 -

functionReturningPromise()
    .then(retVal => doSomething(retVal));

它希望我为此 Promise 添加一个 catch 块。如果我想在异常处理块中添加一些逻辑,这对我来说是有意义的。但是,在很多情况下我不需要它。我对抛出的错误很满意。因此,为了抑制此规则引发的错误,我最终这样做了 -

functionReturningPromise()
    .then((retVal) => doSomething(retVal))
    .catch((error) => {
        throw error;
    });
即使我没有像上面那样添加 catch 块,error 仍然会以相同的方式抛出(至少在我的控制台输出中看到它)。因此,我不明白显式指定此 catch 块的意义。我错过了什么吗?如果我添加/不添加 catch 块,抛出 error 的方式真的有区别吗?

P粉590929392P粉590929392213 天前365

全部回复(1)我来回复

  • P粉106711425

    P粉1067114252024-03-20 17:42:08

    评论者都很好地回答了您的问题 - 但为了通过示例说明为什么这很重要,请想象以下代码:

    Promise.reject();
    setTimeout(() => console.log('hello'), 1000);

    这段代码看起来相当无害 - 有一个未处理的无操作承诺拒绝,启动后 1 秒程序将记录 'hello'

    在浏览器中 - 这正是将会发生的情况 - 浏览器将记录“未捕获的承诺拒绝”错误,但否则会忽略它。

    然而,在 NodeJS(从 Node v15 开始)中,未处理的 Promise 拒绝是一个硬错误 - 这意味着进程在检测到错误时将退出!

    您可以通过在终端中运行代码来验证这一点(-e 的意思是“评估并运行此代码字符串”)

    $ node -e "Promise.reject(); setTimeout(() => console.log('hello'), 1000)"
    node:internal/process/promises:288
                triggerUncaughtException(err, true /* fromPromise */);
                ^
    
    [UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "undefined".] {
      code: 'ERR_UNHANDLED_REJECTION'
    }
    
    Node.js v18.12.1

    您可以看到 'hello' 永远不会被打印,因为进程在 1 秒之前终止!

    要验证事情是否按预期工作,您可以将 .reject 更改为 .resolve

    $ node -e "Promise.resolve(); setTimeout(() => console.log('hello'), 1000)"
    hello

    因此,如果您正在使用任何 LTS 支持的版本编写 NodeJS 应用程序,那么您绝对应该处理错误,否则您将面临意外崩溃的风险。

    如果您的代码仅在浏览器中运行,那么您可能想知道是否应该处理失败 - 毕竟您的用户没有查看控制台,因此他们不会知道发生了任何不好的事情,所以谁在乎呢?从“仅代码”的角度来看,您是正确的 - 但是,当出现问题时,您的用户希望从他们的应用程序中获得反馈。

    想象一下以下场景 - 您的 Promise 跟踪 API 调用的结果,该调用提交用户在您的应用程序中输入的一些数据。如果该 API 调用由于某种原因失败,那么您的应用程序应该做出适当的响应并告诉用户出了问题。

    处理它的替代方法可能意味着您的应用程序显示无限加载旋转器,或者用户认为他们的数据已成功提交,但实际上并未成功。无论哪种方式 - 这是一个非常糟糕且破碎的用户体验!

    最后做一些像 .catch(e => { throw e }) 这样的事情,你实际上并没有处理错误。当然,这段代码会使 linter 保持沉默 - 但您所做的只是创建一个新的、被拒绝的承诺,该承诺将记录到控制台。相反,您应该以某种方式将错误连接到应用程序的 UI,例如像 .catch(e => {alert(e); throw e }) 这样简单的东西会更好。

    回复
    0
  • 取消回复