logo

forEach中使用async/await遇到的问题

Thu Feb 16 2023 Posted 2 years ago

问题描述 #

今天遇到了 forEach 搭配 async/await 使用的问题:

const getTexts = () => {
  return Promise.resolve(['1st', '2nd', '3rd'])
}

const addSuffix = (text) => {
  return new Promise((resolve, reject) => {
    const t = Math.floor((Math.random() * 10) + 1)
    setTimeout(() => {
      if (text)
        resolve(`${text} output`)
      else
        reject(new Error('text not specified'))

    }, t * 1000)
  })
}

const forEachAsync = async () => {
  console.log('ForEach:')
  const texts = await getTexts()

  texts.forEach(async (text) => {
    const res = await addSuffix(text)
    console.log(res)
  })
}

forEachAsync()

这段代码期待的结果应该是按顺序依次打印1st 2nd 3rd,但实际上每次执行 forEachAsync 后打印顺序都是随机的。原因在于 forEach 并不在乎接收的回调函数是一个同步函数还是异步函数,也就是说它的执行流程是同步的,而不会等待 promiseresolve/reject 后才执行下一次循环。

通过 forEach 的 polyfill 我们可以明白原因:

Array.prototype.forEach = function (callback) {
  // this represents our array
  for (let index = 0; index < this.length; index++) {
    // We call the callback for each entry
    callback(this[index], index, this)
  }
}

forEach 内部使用 for 循环来执行异步函数,所以是并行执行。

解决方法 #

方法一 #

使用 for-of 或者 for-in 来替代 forEach

方法二 #

改造 forEach 来使其支持异步函数:

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

}