为什么await 包括所有其他失败的承诺?

kat*_*aik 3 javascript node.js promise async-await typescript

我有一个关于 Promise 在 Node.js 中如何工作的基本问题(在浏览器中,行为符合预期),请考虑以下函数作为示例:

async function proc(): Promise<void> {
  const resolve = new Promise((resolve) => setTimeout(resolve, 0))
  const reject = Promise.reject('reject')

  console.log(await resolve)

  try {
    await reject
  } catch (err) {
    console.error(err)
  }
}
Run Code Online (Sandbox Code Playgroud)

由于拒绝更快地出列,因此它被抛出await resolve,并且由于它没有在那里处理,所以我们得到了未处理的拒绝。

尽管有很多解决方案可以解决这个问题,但我发现这种行为违反直觉。是否有任何原因导致传递给的对象reject()在相应的 Promise 被执行时没有被抛出await,类似于传递给的对象resolve()在 时如何返回await

我期望await做这样的工作:

await(promise) {
  let result, error;

  promise
    .then((data) => result = data)
    .catch((err) => error = err)

  // wait for promise magic

  if (error != null) {
    throw error
  }

  return result
}
Run Code Online (Sandbox Code Playgroud)

rom*_*lem 5

这更多地与 NodeJS 如何响应“未处理的承诺拒绝”有关,其中默认值最近throwNode 15中更改为。

每当发生未处理的 [promise] 拒绝时,都会采取以下步骤(文档):

  1. 发射unhandledRejection
  2. 如果未设置此挂钩,则将未处理的拒绝作为未捕获的异常引发。

因此,如果您有以下脚本:

// example.js
async function proc() {
    const resolve = new Promise((resolve) => setTimeout(resolve, 0));
    const reject = Promise.reject("reject");

    await resolve;
    console.log("Able to `await resolve`");

    try {
        await reject;
    } catch (err) {
        console.error('Caught an error!');
        console.error(err);
    }
}

proc();
Run Code Online (Sandbox Code Playgroud)

执行该脚本的结果会根据mode标志--unhandled-rejections的设置而变化。

如果将其设置为throw(默认值),您会看到程序永远不会继续越过该await resolve行,因为被拒绝的承诺在履行承诺之前抛出。

另请注意,如果您事先设置了process.on('unhandledRejection', ...),您也不会看到被拒绝的承诺抛出。

以下是您运行example.js脚本时可能会看到的内容:

$ node --unhandled-rejections=throw example.js

node:internal/process/promises:279
            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 "reject".] {
  code: 'ERR_UNHANDLED_REJECTION'
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您在模式设置为 的情况下运行它warn,您将看到如下内容:

$ node --unhandled-rejections=warn example.js

(node:55979) UnhandledPromiseRejectionWarning: reject
(Use `node --trace-warnings ...` to show where the warning was created)
(node:55979) 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)
Able to `await resolve`
Caught an error!
reject
(node:55979) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
Run Code Online (Sandbox Code Playgroud)

也就是说,我们能够到达该try块,因为被拒绝的 Promise 不会抛出未捕获的异常。