【问题标题】:Awaiting multiple promises inside an async function with try catch throws anyways在异步函数中等待多个承诺,无论如何都会抛出 try catch
【发布时间】:2020-11-13 07:37:27
【问题描述】:

我不明白为什么下面的代码没有抛出:

const main = async () => {

    const Stop = (time) => new Promise((resolve) => setTimeout(resolve, time))

    try {
        const p1 = Stop(500).then(() => { throw new Error('Error ocurred') })
        const p2 = Stop(1000)

        await p1
        await p2
    } catch (err) {
        console.log('error catched')
    }
}

main()

但每当我颠倒 p1 和 p2 承诺的顺序时,就像这样:

const main = async () => {

    const Stop = (time) => new Promise((resolve) => setTimeout(resolve, time))

    try {
        const p1 = Stop(500).then(() => { throw new Error('Error ocurred') })
        const p2 = Stop(1000)

        await p2
        await p1
    } catch (err) {
        console.log('error catched')
    }
}

main()

然后抛出未捕获的异常。我认为在没有 .catch 函数的情况下执行类似的并发任务是危险的,但我认为 try catch 中的异步代码永远不会抛出。

为什么不是这样呢?

【问题讨论】:

  • 两个 sn-ps 日志 “错误捕获”
  • @adiga 打开真正的浏览器控制台,第二个也会登录Uncaught (in promise) Error
  • @Barmar 你是对的,对不起 OP
  • 没问题!我想澄清一下,不是我不明白为什么会出现捕获的错误,而是第二个示例失败的原因。

标签: javascript concurrency async-await try-catch


【解决方案1】:

我稍微修改了您的代码,它按预期工作。

const main = async () => {

const Stop = (time) => new Promise((resolve) => 
  setTimeout(resolve, time) )


try {
    const p1 = new Stop(500).then(() => { throw new Error('Error ocurred') })
    const p2 = new Stop(1000)

    await p2
    await p1
} catch (err) {
    console.log('error catched')
}} 
main()

我的理解是,Stop 之前指的是同一个实例,当我使用 new 关键字时,它创建了 2 个单独的实例来解决问题

【讨论】:

    【解决方案2】:

    问题在于 async/await 旨在按时间启动异步任务并等待其执行,而在您的 sn-ps 中您同时启动两个异步任务。如果在等待一个任务执行完成时抛出另一个任务,您会得到Uncaught (in promise) Error

    async/await 的一个很好的用法如下:

    const main = async() => {
    
      const Stop = (time) => new Promise((resolve) => setTimeout(resolve, time));
    
      try {
        const p1 = await Stop(500).then(() => { throw new Error('Error ocurred') });
        const p2 = await Stop(1000);
      } catch (err) {
        console.log('caught:', err.message)
      }
    }
    
    main()

    但是这两个任务是按顺序执行的。处理更多并发异步任务的正确方法吧Promise.all

    const main = async() => new Promise((resolve, reject) => {
    
      const Stop = (time) => new Promise((resolve) => setTimeout(resolve, time));
    
      const p1 = Stop(1000).then(() => {
        throw new Error('Error ocurred')
      });
      const p2 = Stop(3000);
    
      Promise.all([p1, p2]).then(resolve).catch(reject);
    });
    
    const parent = async() => {
      const start = new Date().getTime();
      try {
        console.log(await main());
      } catch (err) {
        console.log("caught:", err.message, "after:", new Date().getTime() - start, "ms");
      }
    };
    
    parent();

    【讨论】:

      【解决方案3】:

      首先,让我们退后一步,一起删除等待:

      const main = async () => {
      
          const Stop = (time) => new Promise((resolve) => setTimeout(resolve, time))
      
          try {
              const p1 = Stop(500).then(() => { throw new Error('Error ocurred') })
              const p2 = Stop(1000)
          } catch (err) {
              console.log('error catched')
          }
      }
      
      main()

      我们得到一个未捕获(承诺)错误。这是意料之中的,因为 try/catch 不应处理 Promise 拒绝。

      https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#Description

      await 表达式导致异步函数执行暂停,直到 承诺已解决(即履行或拒绝),并恢复 完成后执行异步函数。恢复时, await 表达式的值是已实现的 Promise 的值。

      如果 Promise 被拒绝,await 表达式会抛出被拒绝的 价值。

      所以如果我们期望 try/catch 来处理 Promise 拒绝,我们需要记住两件事:

      1. 我们需要调用 await 以便在 Promise 被拒绝时抛出表达式(避免 Uncaught (in Promise) 错误)
      2. 我们必须在 Promise 被拒绝之前调用 await

      我们可以通过添加我们的第一个等待来看到这一点:

      const main = async () => {
      
          const Stop = (time) => new Promise((resolve) => setTimeout(resolve, time))
      
          try {
              const p1 = Stop(500).then(() => { throw new Error('Error ocurred') })
              const p2 = Stop(1000)
      
              await p1
          } catch (err) {
              console.log('error catched')
          }
      }
      
      main()

      现在这个await 将在 Promise 拒绝时抛出,我们避免了 Uncaught (in Promise) 错误。

      但是如果我们在await p1 之前添加await p2,则p1 Promiseawait p2await p1 被调用之前会被拒绝。try/catch 无法及时工作,我们无法正确处理 Promise拒绝:

      const main = async () => {
      
          const Stop = (time) => new Promise((resolve) => setTimeout(resolve, time))
      
          try {
              const p1 = Stop(500).then(() => { throw new Error('Error ocurred') })
              const p2 = Stop(1000)
      
              await p2
              await p1
          } catch (err) {
              console.log('error catched')
          }
      }
      
      main()

      我们可以通过改变时间来进一步观察这个关键的等待序列,以便await p2及时恢复函数的执行,以便调用await p1,这样try/catch就在等待await p1扔。

      const main = async () => {
      
          const Stop = (time) => new Promise((resolve) => setTimeout(resolve, time))
      
          try {
              // Increased the time for p1 so the Promise resolves after p2 Promise resolves
              const p1 = Stop(1500).then(() => { throw new Error('Error ocurred') })
              const p2 = Stop(1000)
      
              await p2
              await p1
          } catch (err) {
              console.log('error catched')
          }
      }
      
      main()

      我建议使用Promise.all:

      1. 更容易管理捕获那些烦人的错误
      2. 避免每次使用 await 时多次暂停(这不是您的代码 sn-ps 的问题,因为 p1 Promisep2 Promise 是“并行”运行的,但这是代码中的常见问题)

      const main = async () => {
      
          const Stop = (time) => new Promise((resolve) => setTimeout(resolve, time))
      
          try {
              const p1 = Stop(500).then(() => { throw new Error('Error ocurred') })
              const p2 = Stop(1000)
      
              await Promise.all([p2, p1])
          } catch (err) {
              console.log('error catched')
          }
      }
      
      main()

      【讨论】:

      • 非常感谢马克汤普森!考虑到这一点,我很乐意重构我的所有异步节点登录代码。显然,await 关键字是负责在 async try ... catch 内抛出的关键字。其他答案也很棒!
      猜你喜欢
      • 2020-01-01
      • 2018-02-03
      • 2018-12-20
      • 2021-02-07
      • 2020-11-19
      • 1970-01-01
      • 1970-01-01
      • 2017-06-15
      • 1970-01-01
      相关资源
      最近更新 更多