如何在 JavaScript 循环中使用 async/await

2025-05-26

如何在 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);
  }
}
Enter fullscreen mode Exit fullscreen mode

💡 请注意,如果您想按顺序读取文件,则不能使用 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']);
Enter fullscreen mode Exit fullscreen mode

在上面的代码中,我们有一个名为 的简单异步函数someFunction,它接受一个数组作为参数,迭代该数组并为每个元素发出 API 请求(通过我们的伪 API 函数)。在本例中,我们希望按顺序解析 API 调用。我们希望输出打印以下内容。

// expected
3000
8000
1000
4000
Enter fullscreen mode Exit fullscreen mode

我们看到的结果不是这个输出,而是以下结果

// actual
1000
3000
4000
8000
Enter fullscreen mode Exit fullscreen mode

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 !!✨")
});
Enter fullscreen mode Exit fullscreen mode

是不是很棒?另一种解决序列中 Promise 的方法是使用异步生成器

async function* readFiles(files) {
  for(const file of files) {
    yield await readFile(file);
  }
};
Enter fullscreen mode Exit fullscreen mode

大多数现代浏览器和 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);
  }));
}
Enter fullscreen mode Exit fullscreen mode

每个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
PREV
为什么您的网站大小应小于 14kb
NEXT
每个开发人员都应该知道的 5 个 Git 技巧