为什么我使用 wait Promise.all 会收到未处理的 Promise Rejection

DaD*_*aDo 9 javascript asynchronous node.js promise

这是我的代码的一般结构:

(async () => {
  try {
    const asyncActions = []

    for (let i = 0; i < 3; i++) {
      await new Promise((resolve, reject) => setTimeout(resolve, 1000))

      for (let j = 0; j < 3; j++) {
        asyncActions.push(new Promise((resolve, reject) => setTimeout(reject, 1000)))
      }
    }

    await Promise.all(asyncActions)
    console.log('all resolved')
  }
  catch (e) {
    console.log('caught error', e)
  }
})()
Run Code Online (Sandbox Code Playgroud)

我希望这能捕获 中发生的任何拒绝,asyncActions因为它们应该由 处理Promise.all(),但不知何故它们未被处理?控制台显示以下内容:

(node:9460) UnhandledPromiseRejectionWarning: undefined
(Use `node --trace-warnings ...` to show where the warning was created)
(node:9460) UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:9460) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:9460) UnhandledPromiseRejectionWarning: undefined
(node:9460) UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
...

(node:9460) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
(node:9460) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 2)
...
Run Code Online (Sandbox Code Playgroud)

为什么它们不被 catch 块处理Promise.all()然后被捕获?

我还注意到,当我分别用new Promise(...)justPromise.resolve()和替换 时Promise.reject(),它会捕获错误。这是为什么?这两个变体不是异步的,因此应该以相同的方式工作吗?

Ben*_*aum 9

我们在 Node.js 中检测未处理 Promise 拒绝的方法是使用启发式。

当承诺被拒绝时,我们给用户一个仍然附加侦听器(同步)的机会 - 如果他们不这样做,我们假设它没有被处理并导致unhandledRejection. 这是因为:

  • 不可能知道用户将来是否会附加这样的处理程序(就像在停止问题中一样)。
  • 在绝大多数情况下,这已经足够了,因为最佳实践是始终立即附加侦听器。

因此,您需要始终同步添加 catch 侦听器,以避免未处理的拒绝。

您还可以(在非人为的情况下)通过在 fork 中添加一个空的 catch 侦听器来选择退出:

(async () => {
  try {
    const asyncActions = []

    for (let i = 0; i < 3; i++) {
      await new Promise((resolve, reject) => setTimeout(resolve, 1000))

      for (let j = 0; j < 3; j++) {
        const p = new Promise((resolve, reject) => setTimeout(reject, 1000));
        p.catch(() => {}); // suppress unhandled rejection
        asyncActions.push(p)
      }
    }

    await Promise.all(asyncActions)
    console.log('all fulfilled')
  }
  catch (e) {
    console.log('caught error', e)
  }
})()
Run Code Online (Sandbox Code Playgroud)
  • 有趣的历史花絮#1:我们考虑使用 GC 来检测 unhandledRejections - 人们发现它更令人困惑。
  • 有趣的历史花絮#2:Chrome 做了同样的事情,但如果您添加侦听器,实际上会追溯删除消息。
  • 有趣的历史花絮#3:这个启发式起源于 2013 年的 bluebird。我在这里谈论它。

  • 解决这个问题的另一种方法是——在可以“等待”之前不要产生承诺。所以,“asyncActions”应该是一个产生 Promise 的 thunk 数组,然后你就会有“await Promise.all(asyncActions.map(fn =&gt; fn()))” (2认同)