JavaScript中为什么async/await在forEach里不工作

Node await async

写JavaScript最头疼的就是回调函数,反人类的设计,虽然IO异步机制很牛逼,于是各种轮子开始造起来,目的是让你尽量以同步IO的方式编写异步IO程序,这样的代码易于维护,并且还能保持IO异步的特性。

【国内直连ChatGPT 29元起】
国内直连ChatGPT,Plus会员每月29元起,支持最新o1模型探索更多领域,无需注册OpenAI账号。

其中,async/await就是其中一个轮子,这货目前看起来还是比较受欢迎的,我也经常在使用。今天要正好也要用它发现不工作了,大致代码如下:

app.js

const numbers = [33, 41, 57];

let sum = 0;

const sumFunction = (a, b) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(a + b);
    }, 1000);
  });
};

numbers.forEach(async (number) => {
  sum = await sumFunction(sum, number);
});

console.log(sum);

跑一下

$ node app.js
0

以上输出为0,超出了我们的预期,显然sum在没有算好之前就先打印出来了。Array.prototype.forEach是原始代码,看不到源码,不知到它怎么实现的,所以我大胆猜测它可能是这么实现的:

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

因为没有await callback,所以我们在forEach里的await就没生效,解决这个问题有两种方式:

for替换forEach

添加一个包裹方法wrapFunc,然后在包裹方法加async。如

app.js

const numbers = [33, 41, 57];

let sum = 0;

const sumFunction = (a, b) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(a + b);
    }, 1000);
  });
};

const wrapFunc = async () => {
  for(let i = 0; i < numbers.length; i++) {
    sum = await sumFunction(sum, numbers[i]);
  }

  console.log(sum);
};

wrapFunc();

以上测试结果

$ node app.js
131

封装一个forEach

我们也可以扩展Array.prototype一个自定义的forEach来实现它也是可以的,比如这个方法就叫做asyncForEach

app.js

const numbers = [33, 41, 57];

let sum = 0;

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

const sumFunction = (a, b) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(a + b);
    }, 1000);
  });
};

const wrapFunc = async () => {
  await numbers.asyncForEach(async (number) => {
    sum = await sumFunction(sum, number);
  });
  
  console.log(sum);
};

wrapFunc();

再次测试结果没问题

$ node app.js
131

虽然await/async能解决问题,但看着感觉还是有点不舒服,也不能要求这么高了,这些年ECMAScript不断更新推出一些标准想努力改善回调函数嵌套过深的问题,说不定几年后又推出什么大招就更好了。

分享

TITLE: JavaScript中为什么async/await在forEach里不工作

LINK: https://www.qttc.net/506-javascript-why-async-await-not-working-inside-foreach.html

NOTE: 原创内容,转载请注明出自琼台博客