Node.js 在try/catch块中等待两个承诺将导致;未经处理的拒绝承诺”;

Node.js 在try/catch块中等待两个承诺将导致;未经处理的拒绝承诺”;,node.js,typescript,async-await,Node.js,Typescript,Async Await,我想等待两个并行的承诺。我不想一次又一次地等待每一个承诺(虽然有效,但速度较慢) 出于这个原因,我认为我可以创建两个承诺,首先让它们运行,比如说两个网络请求,然后等待它们,并能够捕获catch块中的错误。这个假设似乎不正确,因为我在运行这个示例代码时收到了一个警告 为什么呢 如何最好地使用优雅的代码并行运行两个或多个网络请求 为什么Typescript不警告我catch块将不可用 抓住拒绝 不会导致“捕获错误”输出,而是得到 tsc test-try-catch-await.ts &

我想等待两个并行的承诺。我不想一次又一次地等待每一个承诺(虽然有效,但速度较慢)

出于这个原因,我认为我可以创建两个承诺,首先让它们运行,比如说两个网络请求,然后等待它们,并能够捕获catch块中的错误。这个假设似乎不正确,因为我在运行这个示例代码时收到了一个警告

  • 为什么呢
  • 如何最好地使用优雅的代码并行运行两个或多个网络请求
  • 为什么Typescript不警告我catch块将不可用 抓住拒绝
不会导致“捕获错误”输出,而是得到

tsc test-try-catch-await.ts && node test-try-catch-await.js

(node:31755) UnhandledPromiseRejectionWarning: b
(node:31755) 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(). (rejection id: 1)
(node:31755) [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.
Caught error b
(node:31755) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

让我们看看我能否以一种可以理解的方式来解释这一点,我们开始吧

创建
b promise
后,您的
setTimeout
-调用的回调(即承诺的拒绝)被“推”到事件循环的
计时器
队列中。现在,当事件循环进入下一次迭代时,承诺队列还没有处理
await bPromise
,但是由于~100毫秒已经过去,回调被执行,您会得到
未处理的承诺拒绝
错误

您需要立即
等待新创建的承诺,因为事件循环的承诺微任务队列这次不会为空,现在将在处理
计时器
-队列中的回调之前执行,因此能够实际捕获错误:

const bPromise = await new Promise((_, reject) => {
      setTimeout(() => reject('b'), 100);
});

我建议阅读并更好地掌握事件循环的工作原理。

这是一种等待几个
Promise
s来解析使用该函数的好方法。它需要一个
Promise
s的
Array
,并生成一个
Promise
,该数组将解析为
Array
,其中包含单个
Promise
s解析为的值。此外,它仅在最后一个
Promise
解析后解析。如果其任何输入
Promise
s被拒绝,则整个
Promise.all
表达式也会被拒绝。它有效地“同时”运行其所有输入进程,模仿经典的“fork-join”模式

出现该错误的原因是,一旦定义了这些
承诺
s,超时过程就会启动,而不是在几行之后等待它们时启动。如果您在一个
承诺
s的定义中记录一些文本,然后在
等待
表达式之前记录其他内容,则可以看到这一点:

async function testMultipleAwait() {
  try {
    const aPromise = new Promise((resolve) => {
      console.log('starting timeout');
      setTimeout(() => resolve('a'), 200);
    });

    const bPromise = new Promise((_, reject) => {
      setTimeout(() => reject('b'), 100);
    });

    console.log('awaiting');
    const a = await aPromise;
    const b = await bPromise;
  } catch (e) {
    console.log('Caught error', e);
  }
}

testMultipleAwait();

// logs:
// "starting timeout"
// "awaiting"
要解决此即时问题,您可以将这些
Promise
s转换为函数,然后调用它们,或
wait
立即:

将这一切结合起来,让它们并行运行,您可以尝试以下方法:

async function testMultipleAwait() {
  try {
    await Promise.all([
      new Promise(resolve => {
        setTimeout(() => resolve("a"), 200);
      }),
      new Promise((_, reject) => {
        setTimeout(() => reject("b"), 100);
      })
    ]);
  } catch (e) {
    console.log("Caught error", e);
  }
}

testMultipleAwait();
我想我知道问题是什么。即使承诺以并行方式启动,您仍在依次等待
aPromise
bPromise

const a = await aPromise; // First await this...
const b = await bPromise; // and then start to await this
当两个承诺都兑现时,这并不是什么大问题。这会让jou等待的时间和承诺的时间一样多。所有的人都会,然后愉快地继续。这就是为什么这个问题不是那么明显的原因

重要的是要知道,由于异步/等待,这个try-catch被转换为承诺。这意味着在等待的语句后面出现的任何内容都将以promise.then回调函数结束

因此,
const b=await b promise
const a
到达之前(200毫秒后)不会运行<代码>b Promise
提前100毫秒失败

这并不是说async/await不会发现错误,也不会将
catch
块(作为promise.catch(…)一起附加到一起。毕竟,节点警告和捕获处理程序都有终端输出:

tsc test-try-catch-await.ts && node test-try-catch-await.js

1 first node sees the error     > (node:31755) UnhandledPromiseRejectionWarning: b
2                                 (node:31755) 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(). (rejection id: 1)
3                                 (node:31755) [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.
4 and then your catch handler   >      Caught error b
5                                 (node:31755) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
因此,decatch子句确实有效,但异步函数在至少200毫秒之后才将其附加到
bPromise
。第5行似乎证实了这一点:

PromiseRejectionHandledWarning: Promise rejection was handled asynchronously.
一旦为空,就会抛出拒绝错误

拒绝承诺已经处理,但node认为你太迟了。您可以使用修复此问题。这样,您将等待一次,异步函数将首先捕获每个潜在错误

// Everything just as it is.. and then:

const [a, b] = await Promise.all([
    aPromise, 
    bPromise,
]);

因为好奇,我在chrome控制台中输入了你的代码,看看会发生什么。一个错误日志会弹出很短的时间(我猜是100毫秒)。看看这个输出,你可以听到chrome说:

“啊,等等!它终于被抓住了。这是消息!”


为什么会超过100毫秒?如果我将时间设置为8000和7000,则会出现相同的错误。请注意,切换计时顺序可以使代码正常工作。奇怪的是,也许我遗漏了什么,我会调查一下——同时,也许某位专业人士可以帮你解决。对不起:)
tsc test-try-catch-await.ts && node test-try-catch-await.js

1 first node sees the error     > (node:31755) UnhandledPromiseRejectionWarning: b
2                                 (node:31755) 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(). (rejection id: 1)
3                                 (node:31755) [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.
4 and then your catch handler   >      Caught error b
5                                 (node:31755) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
PromiseRejectionHandledWarning: Promise rejection was handled asynchronously.
// Everything just as it is.. and then:

const [a, b] = await Promise.all([
    aPromise, 
    bPromise,
]);