五种在循环中使用 async/await 的方法

摘要:我们经常会遇到这样的需求,在循环中使用异步请求,而 ES6 的 async/await 是我们让异步编程更简单的利剑 。本篇总结了 5 种在循环中使用 async/await 的方法(代码干货都能在浏览器控制台自测):

我们经常会遇到这样的需求,在循环中使用异步请求,而 ES6 的 async/await 是我们让异步编程更容易的利剑。
本文总结了5种循环使用async/await的方法(代码可在浏览器控制台自测):

  • 打勾的方法 ✔:表示在循环中每个异步请求是按照次序来执行的,我们简称为 “串行”
  • 打叉的方法 ❌ :表示只借助循环执行所有异步请求,不保证次序,我们简称为 “并行”


forEach ❌

首先,想到遍历,我们常用 forEach,用 forEach 可以吗?来试试~

首先要明确的是,本质上 forEach 就是一个 for 循环的包装。

Array.prototype.forEach = function (callback) {
for (let index = 0; index < this.length; index++) {
  callback(this[index], index, this)
}
}

在回调函数内部调用 await 需要这个回调函数本身也是 async 函数,所以在【循环+ async/await】中的代码应这样写:

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']);

在控制台执行,我们可以看到 forEach 并没有串行输出结果。forEach 只是把所有请求执行了,谓之并行


for...of... ✔

事实上 for...of 却符合我们串行的要求。

思路如下:

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);
}
}

针对本文例,代码如下:

async function someFunction(items) {
for (const i of items){
    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']);

reduce ✔

有了解过【循环】+【异步】的童鞋肯定知道 reduce。它可以称得上是精华所在!

代码如下:

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 !!")
});

我们可以使用 reduce 函数来遍历数组并按顺序 resolve promise。

很清晰!自行控制台体验。


generator ✔

其实用 async generator 也是可以的。

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

针对本文例,代码如下:

async function* generateSequence(items) {
for (const i of items) {
  await new Promise(resolve => setTimeout(resolve, i));
  yield i;
}
}

(async () => {
let generator = generateSequence(['3000','8000','1000','4000']);
for await (let value of generator) {
  console.log(value);
}
})();

自行控制台体验。


Promise.all ❌

如果你不用考虑异步请求的执行顺序,你可以选择 Promise.all(),即 Promise.all() 可以达到 并行 的目的。它也能保证你的请求都被执行过。

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 function promiseAll(arr) {
await Promise.all(arr.map(async (i) => {
  await sleep(i)
  console.log('--->', i);
}))
}

function sleep(i) {
return new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve()
  }, i)
})
}

promiseAll(['3000','8000','1000','4000'])

自行控制台体验。

本文章来自于公众号「掘金安东尼」

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://shenqiku.cn/article/FLY_10710