在forEach循环中使用async/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,所以我只想问问这是否有问题。
当然,代码确实有效,但我很确定它并没有达到您期望的效果。它只是触发多个异步调用,但printFiles函数在这之后会立即返回。
按顺序读取
如果您想按顺序读取文件,则实际上不能使用forEach。只需使用现代for…of循环,其中await将按预期工作:
async function printFiles () {
const files = await getFilePaths();
for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}
并行读取
如果要并行读取文件,则不能使用forEach。每一个异步回调函数调用都会返回一个promise,但您要丢弃它们而不是等待它们。只需使用map,您就可以等待Promise.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)
}));
}
与Antonio Val的p迭代类似,另一种npm模块是异步af:
const AsyncAF = require('async-af');
const fs = require('fs-promise');
function printFiles() {
// since AsyncAF accepts promises or non-promises, there's no need to await here
const files = getFilePaths();
AsyncAF(files).forEach(async file => {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
});
}
printFiles();
或者,async-af有一个静态方法(log/logAF)来记录promise的结果:
const AsyncAF = require('async-af');
const fs = require('fs-promise');
function printFiles() {
const files = getFilePaths();
AsyncAF(files).forEach(file => {
AsyncAF.log(fs.readFile(file, 'utf8'));
});
}
printFiles();
然而,该库的主要优点是您可以链接异步方法来执行以下操作:
const aaf = require('async-af');
const fs = require('fs-promise');
const printFiles = () => aaf(getFilePaths())
.map(file => fs.readFile(file, 'utf8'))
.forEach(file => aaf.log(file));
printFiles();
异步af
当然,代码确实有效,但我很确定它并没有达到您期望的效果。它只是触发多个异步调用,但printFiles函数在这之后会立即返回。
按顺序读取
如果您想按顺序读取文件,则实际上不能使用forEach。只需使用现代for…of循环,其中await将按预期工作:
async function printFiles () {
const files = await getFilePaths();
for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}
并行读取
如果要并行读取文件,则不能使用forEach。每一个异步回调函数调用都会返回一个promise,但您要丢弃它们而不是等待它们。只需使用map,您就可以等待Promise.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)
}));
}
我使用Array.prototype.reduce代替Promise.all和Array.prototy.map(这不能保证Promise的解析顺序),从解析的Promise开始:
async function printFiles () {
const files = await getFilePaths();
await files.reduce(async (promise, file) => {
// This line will wait for the last async function to finish.
// The first iteration uses an already resolved Promise
// so, it will immediately continue.
await promise;
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}, Promise.resolve());
}