JavaScript里循环与异步相遇的几种情况

每当循环和异步遇到一起时,问题就会变得不太容易理解。在过去没有async/await的时代,我们不得不用计数器的方式去实现。以下列举一些常见的实现方式:

“阻塞”执行

const arr = [1, 2, 3, 4, 5, 6]
const genRandom = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve(Math.random()), 200);
  })
}
async function main() {
  for (let i = 0; i < arr.length; i++) {
    arr[i] += await genRandom()
  }
}

这是比较直观的方式,和同步类似一看就懂。缺点嘛也很明显,每次迭代都多余了不必要的阻塞等待时间,效率是个问题。

Promise.all 和 Array.map

async function main() {
  await Promise.all(arr.map(async num => {
    return num + await genRandom()
  }));
}

在不关心执行顺序的情况下,这是一种比较理想的方式。

迭代依赖

Array.reduce场景下,每次迭代会都依赖于前一次,那应该怎么做呢?(这也是我记录下这个小问题的原因)

async function main() {
  const total = await arr.reduce(
    async(prev, current) => {
      // prev是上次return的结果,也是一个Promise,所以需要加上await
      return (await prev) + current + await genRandom()
    },
    0 // 这里的0直观的表达应该是Promise.resolve(0)
  ) 
  console.log(total); // 24.343225147972465
}