将 async/await 与 forEach 循环一起使用

在循环中使用 /是否存在任何问题?我正在尝试遍历一个文件数组以及每个文件的内容。asyncawaitforEachawait

import fs from 'fs-promise'

async function printFiles () {
  const files = await getFilePaths() // Assume this works fine

  files.forEach(async (file) => {
    const contents = await fs.readFile(file, 'utf8')
    console.log(contents)
  })
}

printFiles()

此代码确实有效,但是这会出现问题吗?有人告诉我,你不应该在这样的高阶函数中使用/,所以我只是想问一下这是否有任何问题。asyncawait


答案 1

当然,代码确实有效,但我非常确定它不会做你期望它做的事情。它只是触发多个异步调用,但该函数在此之后会立即返回。printFiles

按顺序读取

如果要按顺序读取文件,则不能使用 forEach。只需改用现代循环,其中将按预期工作:for … ofawait

async function printFiles () {
  const files = await getFilePaths();

  for (const file of files) {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  }
}

并行读取

如果要并行读取文件,则不能确实使用 forEach。每个回调函数调用都会返回一个 promise,但你会丢弃它们,而不是等待它们。只需使用代替,您就可以等待您将获得的一系列承诺:asyncmapPromise.all

async function printFiles () {
  const files = await getFilePaths();

  await Promise.all(files.map(async (file) => {
    const contents = await fs.readFile(file, 'utf8')
    console.log(contents)
  }));
}

答案 2

使用ES2018,您可以大大简化上述所有答案:

async function printFiles () {
  const files = await getFilePaths()

  for await (const contents of files.map(file => fs.readFile(file, 'utf8'))) {
    console.log(contents)
  }
}

请参阅规范:建议-异步-迭代

简化:

  for await (const results of array) {
    await longRunningTask()
  }
  console.log('I will wait')

2018-09-10:这个答案最近引起了很多关注,有关异步迭代的更多信息,请参阅Axel Rauschmayer的博客文章