【问题标题】:Async actions in array.map and for looparray.map 和 for 循环中的异步操作
【发布时间】:2020-08-16 14:08:10
【问题描述】:

当我做这样的事情时我意识到:

for (const entity of someArr) {
  console.log('start now!')
  await doSomeAsycAction()
  console.log('waited X secs!')
}

打印出来:

start now!
waited X secs!
start now!
waited X secs!
...

但是当我使用地图时:

arr.map(async entity => {
  console.log('start now!')
  await doSomeAsycAction()
  console.log('waited X secs!')
})

打印出来:

start now!
start now!
start now!
...
waited X secs!
waited X secs!
waited X secs!
...

有人能解释一下为什么会这样吗?

【问题讨论】:

标签: javascript arrays for-loop asynchronous


【解决方案1】:

两个流程的区别在于第一个流程(使用forfor..infor..of)按顺序运行循环迭代,而另一个流程(使用mapfilter、@987654327 @、forEach 等)同时运行(如果async 符号在映射函数的某处使用)同时运行(有点)。 在for 循环中,下一次迭代必须等待前一次完成。这允许您执行一些与下一次迭代相关的异步操作。 相反,使用异步方法独立运行每个迭代,因此您不能依赖当前迭代中的其他迭代。这类函数接收一个函数作为参数,并立即为数组中的每一项执行它。

将每次迭代视为一个独立的 Promise 执行。运行异步函数时,await 符号表示此操作可能需要一段时间(即 I/O、数据库调用、网络操作......),让当前执行函数之外的代码继续运行(稍后恢复) on,在异步调用返回后)。 map 函数看到当前迭代很忙,然后继续下一个迭代。将来某个时候它会恢复并执行console.log('waited X secs!')

您可以通过这种方式使用for 循环模拟异步执行的相同行为(可能有助于展示差异):

for (const entity of someArr) {
  (async () => {
    console.log('start now!')
    await doSomeAsycAction()
    console.log('waited X secs!')
  })()
}

async-await 语法适用于每个函数范围,map 正在定义一个新的函数范围(作为参数传递给函数的函数),就像在我的示例中每次迭代都会执行的(匿名)函数一样.希望对理解有所帮助。

需要注意的重要一点是map 的每次迭代都不会返回您期望的映射值,而是将使用该值解决的承诺。因此,如果您尝试依赖其中一个映射数组值 - 您必须在其前面添加一个 await,否则该值类型仍然是一个承诺。看看下面的例子:

let arr = [1];
arr = arr.map(async entity => incrementAsync(entity));
console.log(arr[0]) // would print an unresolved Promise object
console.log(await arr[0]) // would print 2

【讨论】:

  • 对于awaiting map(…) 调用的结果,使用Promise.all。不要 await 仅来自数组的单个承诺。
  • @Bergi 这背后有什么合乎逻辑的原因吗? await Promise.all(someArr.map()) 和 await someArr.map() 之间到底有什么区别?
  • @DavidKim await someArr.map(…) 根本不起作用,因为 await 对数组没有任何作用。我担心的是await arr[0]leads to problems with errors from the other promises in the array
  • @DavidKim 两者之间的区别在于如果以下术语不是承诺,await 关键字是无关紧要的。 map 返回一个数组(在上面的例子中,一个 promise 数组),而 awaiting 在一个数组上只返回那个数组。 Promise.all() 用于将一组 Promise 转换为单个 Promise,该 Promise 在完成所有 Promise 时解决(因此即使其中一个失败也会失败)。由于Promise.all()返回了一个promise,你可以在它上面await,这意味着当所有的promise都被解析时,Promise.all()正在被解析。
  • @ShayYzhakov 抱歉,应该更清楚一点,是在讨论等待异步操作数组,而不仅仅是任何数组。所以我的意思是: await someArr.map(async el => {...}) 就像您在示例中所做的那样 await arr[0]
【解决方案2】:

forEach、map、filter、find 等基本数组原型循环函数不等待下一次迭代。它们的基本行为是迭代而不是等待。如果您想使用类似等待功能,请尝试如下使用

for (const event of events) {
  if (failure or conditional) {   
    continue;
  } 
}

【讨论】:

    猜你喜欢
    • 2016-12-28
    • 2021-04-28
    • 2017-05-19
    • 2017-11-03
    • 2019-02-15
    • 2014-02-06
    • 1970-01-01
    • 1970-01-01
    • 2011-11-08
    相关资源
    最近更新 更多