顺序执行异步任务

使用 for await 和 reduce 实现异步任务的顺序执行

问题

如何让多个异步任务按顺序依次执行,而不是并发执行?

解答

方法一:for 循环 + await

普通的 for 循环是顺序执行的,配合 async/await 可以实现异步任务的顺序执行。注意 Array.forEachArray.map 是并发执行的,不适用于此场景。

(async () => {
  const sleep = delay => {
    return new Promise((resolve) => {
      setTimeout(() => resolve(), delay)
    })
  }
  
  const task = (i) => {
    return new Promise(async (resolve) => {
      await sleep(500)
      console.log(`now is ${i}`)
      resolve(++i)
    })
  }
  
  let result = 0
  for (let i = 0; i < 4; i++) {
    result = await task(result)
  }
})()

输出:

now is 0
now is 1
now is 2
now is 3

方法二:Array.prototype.reduce

利用 reduce 的累积特性,将上一个异步任务的结果传递给下一个任务。

const sleep = delay => {
  return new Promise((resolve) => {
    setTimeout(() => resolve(), delay)
  })
}

const task = (i) => {
  return new Promise(async (resolve) => {
    await sleep(500)
    console.log(`now is ${i}`)
    resolve(++i)
  })
}

[task, task, task, task].reduce(async (prev, task) => {
  const res = await prev
  return task(res)
}, 0)

reduce 中:

  • prev 是前一个异步任务返回的 Promise
  • task 是当前要执行的异步任务
  • 通过 await prev 获取上一个任务的结果,作为当前任务的参数

输出:

now is 0
now is 1
now is 2
now is 3

关键点

  • 普通 for 循环是顺序执行的,forEachmap 是并发执行的
  • for + await 方式简单直观,适合固定次数的循环
  • reduce 方式利用累积值传递结果,适合任务数组的场景
  • 每个异步任务需要等待上一个任务完成后才能执行