async/awaitとforEach

1987 ワード

async/awaitはとても使いやすいですが、Array.forEach()にはトラップがあります.
問題の説明
たとえば、次のコードがあります.
const waitFor = (ms) => new Promise(r => setTimeout(r, ms));
[1, 2, 3].forEach(async (num) => {
  await waitFor(1000);
  console.log(num);
});
console.log('Done');

コンソールで上記のコードを実行した結果は次の通りです(node.jsバージョン≧13.0.0).
$ node forEach.js
$ Done
$ 1
$ 2
$ 3

もんだいぶんせきconsole.log(num)の結果は最後に出力され、同時に出力される.forEach()の方法を作成して、何が起こったのかを理解します.
Array.prototype.forEach = function (callback) {
  //          
  for (let index = 0; index < this.length; index++) {
    //        callback   ,      
    callback(this[index], index, this);
  }
};
forEach()のpolyfillインプリメンテーションは、以下を参照してください.
MDN-Array.prototype.forEach() callbackasync/awaitで実行されていないので、Promiseの結果は最後に印刷されないことがわかります.
問題を解決するasyncForEachの代わりに、自分で書いたforEachの方法を使用することができます.
asyncForEach([1, 2, 3], async (num) => {
  await waitFor(50);
  console.log(num);
})
console.log('Done');

上のコードを実行すると、次の結果が表示されます.
$ node forEach.js
$ Done
$ 1
$ 2
$ 3

結果はあまり変わらないようですが、console.log(num)はすでにasync/awaitで実行されており、setTimeoutの効果も見られます.
しかし、まだ理想的ではありません.実際にはasyncForEachPromiseを返します.async関数に包まれているので、実行が完了してから最後のDoneを印刷するのを待つことができます.
const start = async () => {
  await asyncForEach([1, 2, 3], async (num) => {
    await waitFor(50);
    console.log(num);
  });
  console.log('Done');
}
start();

再実行すると、正しい結果が表示されます.
$ node forEach.js
$ 1
$ 2
$ 3
$ Done