在 forEach
循环中使用 async/await
是否有任何问题?我正在尝试遍历每个文件的内容的文件数组和 await
.
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()
这段代码确实有效,但这可能会出错吗?我有人告诉我你不应该在这样的高阶函数中使用 async/await
所以我只是想问这个是否有任何问题 .
12 回答
而不是
Promise.all
与Array.prototype.map
(不保证Promise
的解析顺序)一起使用,而是使用Array.prototype.reduce
,从已解析的Promise
开始:对我来说使用带有
map()
的Promise.all()
有点难以理解和冗长,但是如果你想在普通的JS中做到这一点,我认为这是你最好的镜头 .如果您不介意添加模块,我实现了Array迭代方法,因此可以使用async / await以非常简单的方式使用它们 .
您的案例的一个例子:
p-iteration
与Antonio Val的p-iteration类似,另一个npm模块是async-af:
或者,async-af有一个静态方法(log / logAF),它记录了promises的结果:
但是,该库的主要优点是您可以链接异步方法来执行以下操作:
async-af
在一个文件中弹出几个方法,以序列化顺序处理异步数据并为代码提供更传统的风格,这是非常轻松的 . 例如:
现在,假设已保存在'./myAsync.js',您可以在相邻文件中执行类似下面的操作:
我会使用经过充分测试的(每周数百万次下载)pify和async模块 . 如果您不熟悉异步模块,我强烈建议您查看its docs . 我已经看到多个开发人员浪费时间重新创建它的方法,或者更糟糕的是,当高阶异步方法简化代码时,制作难以维护的异步代码 .
使用Task,futurize和一个可遍历的List,你可以做到
这是你如何设置它
构建所需代码的另一种方法是
或者甚至可能更具功能性
然后从父函数
如果你真的想要更灵活的编码,你可以这样做(为了好玩,我正在使用建议的Pipe Forward operator)
PS - 我没有在控制台上尝试这个代码,可能会有一些错别字......“直接自由泳,离开圆顶顶部!”正如90年代的孩子们所说的那样 . :-P
当然代码确实有效,但是我做了你期望它做的事情 . 它只是触发多个异步调用,但
printFiles
函数会在此之后立即返回 .如果你想按顺序读取文件,确实是 you cannot use forEach . 只需使用现代的
for … of
循环,其中await
将按预期工作:如果你想并行读取文件,确实是 you cannot use forEach . 每个
async
回调函数调用都会返回一个promise,但是你将它们丢弃而不是等待它们 . 只需使用map
,您就可以等待Promise.all
获得的承诺数组:然而,上面的解决方案都可以解决问题,安东尼奥用更少的代码完成工作,这里是它如何帮助我从数据库中解析数据,从几个不同的子引用,然后将它们全部推入一个数组并在一个承诺中解决它毕竟是完成:
一个重要的 caveat 是:
await + for .. of
方法和forEach + async
方式实际上有不同的效果 .在真正的
for
循环中使用await
将确保所有异步调用逐个执行 . 并且forEach + async
方式将同时触发所有承诺,这更快但有时不堪重负( if you do some DB query or visit some web services with volume restrictions 并且不希望一次发射100,000个呼叫) .如果您不使用
async/await
并且想要确保文件被读取 one after another ,您也可以使用reduce + promise
(不太优雅) .或者你可以创建一个forEachAsync来帮助,但基本上使用相同的循环底层 .
以下是一些forEach异步原型:
使用ES2018,您可以大大简化以上所有答案:
见规格:https://github.com/tc39/proposal-async-iteration
2018-09-10:这个答案最近受到了很多关注,请参阅Axel Rauschmayer的博文,了解有关异步迭代的更多信息:http://2ality.com/2016/10/asynchronous-iteration.html
除了@Bergi’s answer,我还想提供第三种选择 . 它与@ Bergi的第二个例子非常相似,但不是单独等待每个
readFile
,而是创建一个promises数组,每个都在等待最后 .请注意,传递给
.map()
的函数不需要是async
,因为fs.readFile
无论如何都会返回Promise对象 . 因此promises
是一个Promise对象数组,可以发送到Promise.all()
.在@ Bergi的回答,控制台可能无序地记录文件内容 . 例如,如果一个非常小的文件在一个非常大的文件之前完成读取,它将被首先记录,即使小文件位于
files
数组中的大文件之后 . 但是,在上面的方法中,您可以保证控制台将按照读取的顺序记录文件 .