Help us understand the problem. What is going on with this article?

async-awaitでもforEachしたい!

More than 1 year has passed since last update.

JavaScript界で非同期処理の切り札的存在となっているasync-await、そして軽やかにループ処理を行っていくforEach。ただ、この2つを合わせて使おうとしたら、うまくいきませんでした。

素直に書いてみる

準備

まずは、非同期実行するもののモデルとして、こんなコードを書いてみます。

const sleep = time => new Promise(resolve => setTimeout(resolve, time));

const sleptLog = async val => {
  await sleep(1000);
  console.log('sleptLog', val);
};

sleepは単にsetTimeoutPromise化しているだけで、そしてsleptLogsleepを使ってasync-awaitconsole.logを遅らせる、というような処理です。

文法的な問題に

次に、これをforEachで使ってみましょう。

const arr = [1, 2, 3];
const testFunc = async () => {
  arr.forEach(item => await sleptLog(item));
  console.log('done!')
};

こんなふうに書きたくなるかもしれませんが、これは文法エラーです。awaitは、asyncな関数の中に直接書くことしかできないのです。

書き換えても意図通りにはならず

ということで、forEachのコールバックもasyncにしてみます。

const testFunc = async () => {
  arr.forEach(async item => await sleptLog(item));
  console.log('done!')
};

とりあえず文法エラーは解決したのですが、これを実行するとdone!のほうが先に出力されてしまいます。forEachの前にawaitを付けても同じです。

原因と対策

原因

forEachは何が来ようが、コールバックの返り値を無視します。結果、async関数が生成したPromiseも無視されて、awaitされることもなく進んでしまいます。

シンプルなやり方には罠がある

関数を切らない、単なるforfor-infor-ofなどであればawaitはされます。

const testFunc = async () => {
  for(let item of arr) await sleptLog(item);
  console.log('done!')
};

ただ、実際に実行してみると気づくかと思いますが、1つ目の実行が終わってから2つ目のタイマーが始まる、というように直列的な動きとなります。あえて使う分には便利な場面もあるかとは思いますが、通信など並列で実行したいという場合には、この方法は向きません。

ライブラリに、あったじゃない!

「複数のPromiseを並列に走らせる」関数といえば、Promise.allが標準で用意されています。今度は、これを使ってみましょう。

const testFunc = async () => {
  await Promise.all(arr.map(async item => await sleptLog(item)))
  console.log('done!')
};

配列をmapして要素をPromiseに変換して、それをPromise.allに投げ込むことで、「配列の中身すべてについてresolveまで待たせる」ことが実現できました。

まとめ

async-awaitも中身はPromiseです。状況によっては、Promiseレベルでハンドリングしたほうが便利なこともあります。

jkr_2255
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした