循环中的Async/Await

假如你需要对多个文件进行操作,最直接的做法就是使用类似forEach方法:

const files = ['./a.txt', './b.txt', './c.txt']
files.forEach(file => {
  const r1 = await task1(file)
  const r2 = await task2(r1)
})

上面的代码存在两个问题:

  1. 传入forEach方法中的函数没有使用async关键字,所以我们不能在其内部使用await关键字。
  2. 即使我们使用async关键字来定义传入到forEach方法中的函数,forEach方法也不会等待所有的异步任务都完成。

forEach方法相反,其他两个循环可行的:

  1. C语言风格的for循环
async function main() {
  const files = ['./a.txt', './b.txt', './c.txt']
  for (let i = 0; i < files.length; i++) {
    const r1 = await task1(files[i])
    const r2 = await task2(r1)
  }
  return 'done'
}
  1. for of循环
async function main() {
  const files = ['./a.txt', './b.txt', './c.txt']
  for (const file of files) {
    const r1 = await task1(file)
    const r2 = await task2(r1)
  }
  return 'done'
}

上面两种方式的弊端是,我们无法并行的执行任务,尽管它们都是异步任务。例如,如果第一个文件比较大,最后一个文件比较小,main函数仍然会操作第一个文件完毕之后才会处理其他任务。更高效的做法是,我们把每个任务都定义为promise,然后使用promise.all来处理它们:

const files = ['./a.txt', './b.txt', './c.txt']
async function operation(f) {
  const r1 = await task1(f)
  const r2 = await task2(r1)
  return r2
}
const tasksPromises = files.map(operation)
Promise.all(tasksPromises)
  .then(r => console.log(r))
  .catch(e => console.log(e))
  .then(_ => console.log('all done'))

上面的代码中,我们使用一个异步函数来定义两个串行执行的异步任务,然后使用了map方法生成了由异步函数组成的数组,最后,使用promise.all来并发处理这些异步任务。注意,数组中的异步任务的处理不是按照特定顺序执行的,但是operation函数中的两个异步任务是按照我们想要的顺序执行的。

用户头像
登录后发表评论