如何在 JavaScript 循环中使用 async/await
迭代项目和处理异步逻辑(即 API 调用)可能是 JavaScript 开发者最常执行的两项任务。在本文中,我们将讨论结合 async/await 和迭代逻辑的最佳方法。有时,您可能希望在 for 循环(或任何其他类型的循环)中运行异步操作。让我们来看看如何处理这种情况。
更新:我也上传了基于这篇文章的视频。
按顺序阅读承诺
假设我们有一个文件列表,我们想按顺序读取并记录每个文件的内容。该怎么做呢?我们可以在异步函数中使用 for ... 循环。以下是代码片段。
async function printFiles () {
let fileNames = ['picard', 'kirk', 'geordy', 'ryker', 'worf'];
for (const file of fileNames) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}
💡 请注意,如果您想按顺序读取文件,则不能使用 forEach 循环。
让我们用一个简单的例子来详细说明这一点。
async function someFunction(items) {
items.forEach( async(i) => {
const res = await someAPICall(i);
console.log('--->', res);
});
}
function someAPICall(param) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve("Resolved" + param)
},param);
})
}
someFunction(['3000','8000','1000','4000']);
在上面的代码中,我们有一个名为 的简单异步函数someFunction
,它接受一个数组作为参数,迭代该数组并为每个元素发出 API 请求(通过我们的伪 API 函数)。在本例中,我们希望按顺序解析 API 调用。我们希望输出打印以下内容。
// expected
3000
8000
1000
4000
我们看到的结果不是这个输出,而是以下结果
// actual
1000
3000
4000
8000
forEach 循环不是按顺序执行 API 调用,而是逐个设置 API 调用。它不会等待前一个调用完成。这就是为什么我们会得到最先解析的 Promise。这也是我们不能使用 forEach 循环的主要原因。
相反,我们可以使用 reduce 函数来迭代数组并按顺序解析 Promise。让我们快速看一个例子。
function testPromise(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`Processing ${time}`);
resolve(time);
}, time);
});
}
let result = [3000,2000,1000, 4000].reduce( (accumulatorPromise, nextID) => {
return accumulatorPromise.then(() => {
return testPromise(nextID);
});
}, Promise.resolve());
result.then(e => {
console.log("All Promises Resolved !!✨")
});
是不是很棒?另一种解决序列中 Promise 的方法是使用异步生成器。
async function* readFiles(files) {
for(const file of files) {
yield await readFile(file);
}
};
大多数现代浏览器和 Node 10 及更高版本都支持生成器。您可以点击此处了解更多关于 JavaScript 中生成器和迭代器的信息。
并行解决 Promise
接下来,我们来看看如何并行地解析 Promise。回到第一个例子。我们不再按顺序读取文件,而是并行读取它们。在这种情况下,我们不关心内容在控制台中打印的顺序。因此,我们可以简单地使用Promise.all()函数和一个map。
async function printFiles () {
let fileNames = ['picard', 'kirk', 'geordy', 'ryker', 'worf'];
await Promise.all(fileNames.map(async (file) => {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}));
}
每个async
回调函数调用都会返回一个承诺,我们将它们存储起来并与同时解决它们Prmiss.all()
。
希望这篇快速阅读能让你了解如何在循环中使用异步代码。如果你喜欢这篇文章,请在 Twitter 上关注我@HaqueShadid。今天就到这里,下次再见。
参考:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop
https://css-tricks.com/why-using-reduce-to-sequentially-resolve-promises-works/
文章来源:https://dev.to/shadid12/how-to-use-async-await-inside-loops-in-javascript-4dlg