Promise.all, allSettled, race, anyの違いと使い方を実例で解説(非同期処理の並列実行, 初心者向け)

javascript-prograshi(プロぐらし)-kv JavaScript
記事内に広告が含まれていることがあります。

JavaScriptの非同期処理を並列で実行したいとき、「Promise.allを使えばいい」と聞いたことはありませんか? しかし、他にもallSettled、race、anyというメソッドがあり、これらは全て並列実行を実現します。

もちろん、複数のAPIからデータを取得したり、時間がかかる処理を同時に走らせたりする際、Promise.allは非常に便利です。

しかし、「一部の処理が失敗しても結果は全て知りたい」「最も速い結果だけを採用したい」といった、Promise.allでは対応できない複雑なニーズに直面することもあります。

この記事では、Promise.allではできない非同期処理の並列実行を可能にする、allSettled、race、any特性や違いをまとめています。


Promise.all, allSettled, race, anyの違い

PromiseのメソッドであるallallSettledraceanyはいずれも、複数の非同期処理を並列で実行するために使います

「いつ完了とみなすか」と「失敗が起きた時にどう振る舞うか」が異なります


メソッド完了条件失敗の振る舞いユースケース
Promise.all()全て成功すること(失敗は例外)1つでも失敗したら、即座に全体が失敗として処理を中断する必要なデータや処理が全て揃わないと次に進めない場合
Promise.allSettled()全て完了すること(成功/失敗問わず)失敗しても処理は中断せず、全ての結果(成功・失敗)を返す全ての処理の結果を把握・記録したい場合
Promise.race()どれか1つが完了すること(成功/失敗問わず)最初に完了したのが失敗なら、即座に全体が失敗として処理される複数の候補から最速の結果だけを採用したい場合
Promise.any()どれか1つが成功すること全てが失敗した場合のみ、全体が失敗として処理される複数の候補からどれか1つでも成功した結果が欲しい場合


Promise.all|一つのチーム(連帯責任)

Promise.all()は、渡されたPromiseを一つのチームと考えます。

allにとって、成功はチームのメンバー全員が成功すること。一人でも失敗したら連帯責任で全員失敗となります。

このため、一人でも失敗した時点で処理を中断します。

例えば、以下のように3つのPromise、p1, p2, p3があるとします。これをPromise.all([p1, p2, p3])で処理すると、p2が失敗するため、結果は失敗となります。

const p1 = Promise.resolve('成功A');
const p2 = Promise.reject(new Error('失敗B')); //失敗を返す
const p3 = Promise.resolve('成功C');

Promise.all([p1, p2, p3])
  .then(results => console.log('結果:', results)) // 実行されない
  .catch(error => console.error('エラー:', error.message)); 

なお、p1, p2, p3はほぼ同時に実行されていますが、p2の処理がエラーが出た時点でPromise.allの処理はエラーを返し終了となります。

※本来はp1, p2, p3はそれぞれ、どこかにデータを取りに行くといった時間のかかる処理をしています。


Promise.allSettled|完全に待つ

Promise.allSettled()は、成功・失敗に関係なく、全てのメンバーの最終結果を待ちます。

allSettledにとって、成功は全員が「完了(settled)」状態になることです。このため、途中で失敗が起きても処理は中断されず、最後まで実行されます。

const p1 = Promise.resolve('成功A');
const p2 = Promise.reject(new Error('失敗B')); 
const p3 = Promise.resolve('成功C');

Promise.allSettled([p1, p2, p3])
  .then(results => {
    results.forEach(result => {
      console.log(`ステータス: ${result.status}`);
      if (result.status === 'fulfilled') {
        console.log(`  値: ${result.value}`);
      } else {
        console.log(`  理由: ${result.reason.message}`);
      }
  });
});

p2でエラーが出ますが、処理が中断せず最後まで進みます。


Promise.race|最速を競うレース

Promise.race()は、渡されたPromiseの中で一番早く完了したもの(成功でも失敗でも)の結果だけを採用します

raceにとっての成功は誰か一人が成功または失敗でゴールすることです。このため、最初に完了したPromiseの値、またはエラーが返され、その時点で処理が終了します。

const slow = new Promise(resault => setTimeout(() => resault('遅い'), 300));
const fast = new Promise(resault => setTimeout(() => resault('速い'), 100));

Promise.race([slow, fast])
  .then(result => console.log('勝者:' + result));

この場合、後に記述してあるfastの方が早く終わるので、その結果を返します。


Promise.any|誰か一人成功すればOK

Promise.any()は、渡されたPromiseの中で最初に成功した結果だけを採用します

anyにとっての成功は誰か一人が成功することです。

途中で失敗が起きても無視され、次の成功を待ちます。全てのPromiseが失敗した場合にのみ、全体が失敗(AggregateErrorという特殊なエラー)として処理されます。

複数のサーバーにリクエストを送り、一番早く返ってきた成功データを使いたい場合などに使います。


例えば、以下の場合、複数のPromiseのうち最初に成功するのはp3なので、p3の結果のみを返します。

const p1 = Promise.reject(new Error('失敗A')); //失敗を返す
const p2 = Promise.reject(new Error('失敗B')); //失敗を返す
const p3 = Promise.resolve('成功C');
const p4 = Promise.resolve('成功D');

Promise.any([p1, p2, p3, p4])
  .then(results => console.log('結果:' + results)) 
  .catch(error => console.error('エラー:' + error.message)); 


なお、全てエラーになると、「All promises were rejected」というメッセージを返します。

const p1 = Promise.reject(new Error('失敗A')); //失敗を返す
const p2 = Promise.reject(new Error('失敗B')); //失敗を返す

Promise.any([p1, p2])
  .then(results => console.log('結果:' + results))
  .catch(error => console.error('エラー:' + error.message)); 
タイトルとURLをコピーしました