遍历执行异步任务(forEach - await , for of - await ,  for-await-of ,  reduce)

遍历执行异步任务(forEach - await , for of - await , for-await-of , reduce)


javascript 异步 遍历

异步任务的遍历

业务中会碰到一些场景,需要遍历异步任务,同时希望它们依次执行,即任务一执行结束后再执行任务二,下面探讨一下几种实现方式的可行性区别

1.forEach + await

function getDatas(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(data);
    }, 1000);
  });
}
let arrays = [1, 2, 3];
arrays.forEach(async (item, index) => {
  console.log(`第${index + 1}次开始`);
  let datas = await getDatas(item);
  console.log("返回结果:" + datas);
  console.log(`第${index + 1}次结束`);
});

打印结果

第1次开始;
第2次开始;
第3次开始;

一秒后再次打印:

返回结果:1
第1次结束
返回结果:2
第2次结束
返回结果:3
第3次结束

可以发现几次执行结果是同时打印出来的,而不是打印1之后过一秒再打印2,所以这种方式不可行,同样的,map, some之类的方法也一样,基本上可以理解为:以回调处理的相关方法里,内部写异步都不能达成效果

2. for 循环/for of + await

function getDatas(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(data);
    }, 1000);
  });
}
let arrays = [1, 2, 3];
async function exec() {
  for (let index = 0; index < arrays.length; index++) {
    let item = arrays[index];
    console.log(`第${index + 1}次开始`);
    let datas = await getDatas(item);
    console.log("返回结果:" + datas);
    console.log(`第${index + 1}次结束`);
  }
}
exec();

打印结果

第1次开始;

一秒后再次打印:

返回结果:1
第1次结束
第2次开始

一秒后再次打印:

返回结果:2
第2次结束
第3次开始

一秒后再次打印:

返回结果:3
第3次结束

这就是我们想要实现的效果,说明for 循环 + await可行。

假设把中间for循环的代码改为for-of

async function exec() {
  let index = 0;
  for (let item of arrays) {
    console.log(`第${index + 1}次开始`);
    let datas = await getDatas(item);
    console.log("返回结果:" + datas);
    console.log(`第${index + 1}次结束`);
    index++;
  }
}

可以发现打印结果跟上面的for 循环 + await结果相同,说明for-of + await 也可行。

造成以上两种实现方式结果差异的原因是:forEach 是直接调用回调函数,for…of 是通过迭代器的方式去遍历。

3. for-await-of

function getDatas(times) {
  times = times || 0;
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(times);
    }, times * 1000);
  });
}
let arrays = [getDatas(2), getDatas(3), getDatas(1)];
async function exec() {
  for await (let item of arrays) {
    console.log(item);
  }
}
exec();

打印结果

2 秒后打印:

第1次开始
返回结果:2
第1次结束

一秒后再次打印:

第2次开始
返回结果:3
第2次结束
第3次开始
返回结果:1
第3次结束

for-of + await的例子改为类似的形式:

function getDatas(times) {
  times = times || 0;
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(times);
    }, times * 1000);
  });
}
let arrays = [getDatas(2), getDatas(3), getDatas(1)];
async function exec() {
  let index = 0;
  for (let item of arrays) {
    console.log(`第${index + 1}次开始`);
    let datas = await item;
    console.log("返回结果:" + datas);
    console.log(`第${index + 1}次结束`);
    index++;
  }
}
exec();

打印结果

立即打印:

第1次开始;

2 秒后再次打印:

返回结果:2
第1次结束
第2次开始

1 秒后再次打印:

返回结果:3
第2次结束
第3次开始
返回结果:1
第3次结束

通过打印结果可以看到,两者是有区别的,for-of + await 是因为代码块中有 await 导致等待 Promise 的状态而不再继续执行,而 for-await-of 是整个代码块都不执行,等待 arrays 中当前遍历到的值(Promise 状态)发生变化之后,才执行代码块的内容。

tip: 使用for-await-of时,如果某一次遍历中碰到了rejected状态的promise,后面的遍历代码块就不会执行了,前面的还是会正常执行。

4. reduce

除了以上几种方式,reduce方法也可以实现依次执行异步任务的效果,实现的方案是构造串行的异步任务。

© 2025 Niko Xie