【问题标题】:using async await with map does not work as expected使用带有地图的异步等待无法按预期工作
【发布时间】:2021-07-29 20:01:15
【问题描述】:

假设我有这个代码:

const array = [1, 2, 3]

let counter = 0

array.map(async (item) => {
  console.log(await item, ++counter)
  console.log(await item, ++counter)
})

预期的输出是

1, 1
1, 2
2, 3
2, 4
3, 5
3, 6

但我得到的是这个

1, 1
2, 2
3, 3
1, 4
2, 5
3, 6

似乎第一个 await 调用首先针对整个阵列运行,然后第二个正在运行,为什么会发生这种情况?

【问题讨论】:

  • 你为什么期待初始输出?
  • 你为什么需要await 一个号码?
  • 这只是简化@Pointy的更大问题的一部分
  • 异步函数在 await 处暂停。这就是关于你的问题的全部解释。但是解决你的问题真的有用吗?您很可能不应该在 .map() 中使用异步。除非你把它放在Promise.all()
  • @Keith,谢谢你,我一定会读到的。

标签: javascript node.js arrays async-await promise


【解决方案1】:

除了异步部分

不要将Array.map 用作命令式循环。请改用Array.forEach

  • Array.map 转换每个值(以任何顺序 (!))。
  • Array.forEach 按顺序调用每个值。

实际上,我想您可以假设Array.map 是顺序执行的,但这样做是不好的做法。

异步部分

您期望Array.map 等待两个console.log 语句完成,然后再调用下一项。这对于Array.mapArray.forEach 都不是正确的,因为尽管代码可能看起来是连续的,但它是异步的。在 Jav 中

如果要按顺序执行异步函数,基本上有两种方式:

使用for循环

(async () => {
  for(var i=0; i<array.length; i++) {
    console.log(await array[i], ++counter)
    console.log(await array[i], ++counter)
  }
})()

使用Array.reduce

array.reduce(async (p, item) => {
  await p
  console.log(await item, ++counter)
  console.log(await item, ++counter)
}, Promise.resolve())

【讨论】:

  • 也许值得指出Array.reduce,它创建了一个叫做Promise 链的东西。如果 for 循环没有,对于非常大的数组,for 循环会更好,并且更灵活,因为 break / continue 也可以按预期工作。
【解决方案2】:

mapforEach 等数组方法无法感知等待。换句话说,它们会执行回调函数,而不管它们内部使用的await 关键字。

这是一个测试:

const array = [1, 2, 3]

array.map(async (item) => {
  console.log('Immediate Item: ', item);
  console.log('Awaited item', await Promise.resolve(item));
})

从结果中可以看出,所有 3 个“立即项”都先注销,然后是“等待”项。

如果您需要一个尊重 async/await 的循环,请使用 for-of 循环。

async function main() {

  const array = [1, 2, 3]

  for (let item of array) {
          console.log('Immediate Item: ', item);
          console.log('Awaited item', await Promise.resolve(item));
    }

}

main();

【讨论】:

    【解决方案3】:

    这是一个(希望)更好的插图。问题是,map 不会等待其回调(“映射器”)完成。它只是为每个元素触发它。所以,当map完成时,我们最终会出现N个mapper,每个mapper都在等待await完成,因为await即使与非承诺一起使用,仍然意味着等待,即等待退出当前的“执行上下文”。

    const array = ['A', 'B', 'C']
    
    let counter
    
    function item(arg) {
        console.log('    CALLED item', arg, ' => ', counter++)
    }
    
    console.log('SYNC: before map')
    counter = 0
    array.map(async (x) => {
        console.log('  begin SYNC mapper', x)
        item(x);
        console.log('    1st item of', x, 'done')
        item(x);
        console.log('  end SYNC mapper', x)
    })
    console.log('after map')
    
    console.log('--------------------------')
    
    console.log('ASYNC: before map')
    counter = 0
    array.map(async (x) => {
        console.log('  begin ASYNC mapper', x)
        await item(x);
        console.log('    1st item of', x, 'done')
        await item(x);
        console.log('  end ASYNC mapper', x)
    })
    console.log('after map')
    
    console.log('execution context ended')
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

      猜你喜欢
      • 2023-03-03
      • 2019-04-19
      • 2019-12-29
      • 2020-03-04
      • 2019-04-07
      • 2018-05-05
      • 2017-08-28
      • 2022-01-22
      • 2018-12-02
      相关资源
      最近更新 更多