在等待循环时无法捕获错误

时间:2019-04-17 18:44:46

标签: node.js async-await

我正在读described on MDN的“等待循环”。它看起来令人印象深刻,所以我玩了一些代码,但令人惊讶的是我无法捕捉到那里抛出的错误。

'use strict';

const main = async() => {
  const bar = [];
  bar.push(new Promise((res) => { setTimeout(() => res(1), 1200); }));
  bar.push(new Promise((res) => { setTimeout(() => res(2), 800); }));
  bar.push(new Promise((res, rej) => { setTimeout(() => rej(3), 200); }));

  try {
    for await (const foo of bar) {
      console.log('hey', foo);
    }
  } catch (err) {
    console.error('err', err);
  }
};

main();

主要是我的输出与我期望的一样。但是我不明白,为什么我得到了UnhandledPromiseRejection?我什至没有抓住那个错误?

$> node await-loop.js 
(node:10704) UnhandledPromiseRejectionWarning: 3
(node:10704) 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:10704) [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.
hey 1
hey 2
err 3
(node:10704) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

有人可以帮我理解并在此处写下正确的流行语吗?我想念什么吗?

2 个答案:

答案 0 :(得分:0)

  

未处理的承诺否决?

     

我什至没有抓住那个错误?

从理论上讲,是的。 (那么,那是catch块的正确选择?)

技术上没有。

Main Stack
|  try             |
|                  |
|    await bar[0]  | <-- waiting to resolve till sometime after 1200
|                  |
|  catch           |
|                  |

(大约)200毫秒后

bar [2]被拒绝并被推送到队列,但是等待仍未屈服,因为它仍在等待bar [0]屈服,只有在1200毫秒后才会发生

因此,在拒绝bar [2]的时间中,没有catch块可用来处理rejected promise。请参见上面的堆栈,请注意,在拒绝时,try..catchbar[2]不在同一堆栈中,至少与编译器(或概念上针对javascript)不一样。

但是您注意到:err 3最终等待时捕获块捕获的bar[2]

如果更改承诺的顺序并将拒绝的承诺作为数组中的第一项,您会看到catch块很好地工作。

或者等待不同的时间:

// unhandled rejection
try {
  await bar[0]
  await bar[1]
  await bar[2]
} catch (e) {
  console.error('error', e)
}

// Catch successfull
try {
  await bar[2]
  await bar[0]
  await bar[1]
} catch (e) {
  console.error('error', e)
}

更新:

更好的方法是使用Promise.all,它具有快速失败的行为。

try {
  const datas = await Promise.all(bar)
} catch (e) {
  console.error(e)
}

答案 1 :(得分:0)

for await of循环只是同步for of循环的简写语法,循环主体中有await。因此,您可以通过以下方法实现所需的目标:

'use strict';

const main = async() => {
  const bar = [];
  bar.push(new Promise((res) => { setTimeout(() => res(1), 1200); }));
  bar.push(new Promise((res) => { setTimeout(() => res(2), 800); }));
  bar.push(new Promise((res, rej) => { setTimeout(() => rej(3), 200); }));

  for (const foo of bar) {
    try { 
      const result = await foo;
      console.log('hey', result);
    } catch (err) {
      console.error('err', err);
    }
  }
};

main();

顺便说一句,如果您想知道引发错误的承诺,那么Promise.allSettled可能更接近您想要的内容和更简洁的代码:

'use strict';

const main = async() => {
  const bar = [];
  bar.push(new Promise((res) => { setTimeout(() => res(1), 1200); }));
  bar.push(new Promise((res) => { setTimeout(() => res(2), 800); }));
  bar.push(new Promise((res, rej) => { setTimeout(() => rej(3), 200); }));

  const results = await Promise.allSettled(bar);
  
  /* Outputs:
  [
  { status: 'fulfilled', value: 1 },
  { status: 'fulfilled', value: 2 },
  { status: 'rejected', reason: 3 }
]
  */
  console.log(results);
};

main();