Promise.all()で非同期処理を並列に捌く

最近趣味でアニメ視聴管理アプリを開発しているのですが、「ループ処理の中で非同期処理を並列で実行して、全ての処理が終わった上で次の処理に移行する」みたいなパターンが多いです。

非同期処理と言えばPromiseの出番ですが、今回はその中でも上記のようなケースで役に立つPromise.all()の使い方を備忘録として残しておきます。

Promiseについて

Promiseそのものについて触れると長くなってしまうので、まずは参考サイトをどうぞ。

簡単に言うと、非同期処理の完了イベント(orエラー)を受け取ることができたり、非同期処理の実行順序を明示的に制御できる仕組みです。
es6から追加された機能なので対応していないブラウザもありますが、何かしらpolyfill通せば問題ないです。

JavaScriptを嗜む方々は「コールバック地獄 」という不穏な言葉を聞いたことがあるかと思いますが、それを解決してくれる技術でもあります。
最も基本的なパターンだとこんな感じになります。

const excutePromise = new Promise((resolve, reject) => {
  // 処理の完了まで数秒かかるような処理
    // 通信成功&レスポンスも受け取れた場合
      // resolve("成功しました");
    // エラーハンドリング
      // reject("失敗しました");
});

excutePromise.then((resultText) => {
  console.log(resultText); //『成功しました』or『失敗しました』
});

ではこの非同期処理を1000回実行した後に、完了を知らせるアラートを表示しようとする場合はどうしましょうか。
forで回せばいけそうですね!

const excutePromise = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, 1000)
  }); 
};

for (let i = 0; i < 1000; i++) {
  excutePromise();
}
alert('全部終わりました!');

こういうソースを無邪気な顔してリリースしようとしていた時期が自分にもありました。
JavaScriptは上から下に向かって同期的に処理が実行されるので、forで1000回処理する準備をしている間にalertが走ってしまいます。

そこで、Promise.all()を使って全ての処理が終わったタイミングを受け取った上でalertを実行します。

Promise.all()メソッド

複数のPromiseを一つの配列に格納して渡すことで、並列的に処理を実行し全ての処理が完了したタイミングで結果を返してくれるものです。

今回の例題で言うと、一回の処理が完了するのに1000msかかる非同期処理が1000回並列的に実行された後にPromise.all()からthenを経由して完了のキューを受け取ることができます。

こんな感じになります。

// Promise.all()に渡すための配列を用意
let promiseArray = [];

// Promiseをreturnする関数として定義しておく
const excutePromise = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, 1000)
  }); 
};

// 初めに作った配列にexcutePromise関数を1000個格納する
for (let i = 0; i < 1000; i++) {
  promiseArray.push(excutePromise())
}

// 1000回分のexcutePromiseを並列的に実行し、全て完了したらalert
 Promise.all(promiseArray).then(() => {
  alert('全部終わりました!');
});

使いどころ

何かしらのデータのIDが格納された配列をグルグル回してAPIから各々に対応するデータを取得する場合などに重宝します。
自分の場合はfirebaseのRealtimeDatabaseからkeyでリレーションさせてデータを取得する際に助けられました。

ただ、あくまで並列処理を前提としているので、例えば「処理Aの結果を持って処理Bを実行。処理Bの結果を持って処理C…」みたいに実行順序を担保しないといけない場合には使用できないのでご注意ください。

おわり

Promiseについては自分もまだまだ初心者なので学習も兼ねて次回以降も書いていきたいと思います。

ではでは