【问题标题】:Unable to catch error in a for await of loop无法在 for await 循环中捕获错误
【发布时间】:2019-09-08 02:07:25
【问题描述】:

我正在阅读“for await of loop”作为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)

有人可以帮我理解并在这里写一个正确的流行语吗?我错过了什么吗?

【问题讨论】:

    标签: node.js async-await


    【解决方案1】:

    for await of 循环只是循环体中带有await 的同步for of 循环的简写语法。所以你可以用这个实现你想要的:

    '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();
    

    【讨论】:

    • 感谢您的回答!但解决这个问题,就是:一种解决方法。
    【解决方案2】:

    UnhandledPromiseRejection?

    我没有发现那个错误吗?

    理论上,是的。 (这就是 catch 块的用途吧?)

    技术上没有。

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

    (大约)200 毫秒后

    bar[2] 被拒绝并推入队列,但 await 没有让出,因为它仍在等待 bar[0] 让出,这仅在 1200 毫秒后发生

    因此,当时 bar[2] 被拒绝没有可用的 catch来处理 rejected promise。请参阅上面的堆栈并注意 try..catchbar[2] 在被拒绝时不在同一堆栈中,至少对于编译器而言(或概念上对于 javascript)。

    但你注意到:err 3,catch 块在最终 await 时捕获到 bar[2]

    如果您更改 promise 的顺序并将被拒绝的 promise 作为数组中的第一项,您会看到 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)
    }
    

    【讨论】:

    • 感谢您的回答!您的解决方案实际上并不实用,因为我实际上需要 知道 任何异步函数正在引发错误(或者在这种情况下拒绝承诺)。你介意在不重新安排我的电话的情况下捕获该错误的解决方案吗?
    • 再次感谢您更新您的答案。但我认为你没有抓住我的方法。我实际上是在使用for await of 循环时尝试捕获错误。如果平易近人,我当然会使用Promise.all。我的示例应该只展示重现我的情况所需的内容。我仍然希望使用for await of 循环,循环异步函数并且仍然能够在错误发生时捕获它们。
    • 就像我在解决方案中所说的那样,这是不可能的。 async/await 只是一个语法糖。它在幕后使用generators。在等待对yield 的承诺时,数组中的其他一些承诺可能已经失败,因此在生成器函数中,范围内不会有 catch 块来处理拒绝
    猜你喜欢
    • 1970-01-01
    • 2015-11-20
    • 1970-01-01
    • 2015-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-31
    相关资源
    最近更新 更多