首页 > 解决方案 > array.map 和 for 循环中的异步操作

问题描述

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

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!
...

有人可以解释为什么会这样吗?

标签: javascriptarraysfor-loopasynchronous

解决方案


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

将每次迭代视为独立的承诺执行。运行异步函数时,该await符号表示此操作可能需要一段时间(即 I/O、数据库调用、网络操作......),让当前执行函数之外的代码继续运行(稍后恢复,之后异步调用返回)。该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

推荐阅读