JavaScriptの非同期処理を並列で実行したいとき、「Promise.allを使えばいい」と聞いたことはありませんか? しかし、他にもallSettled、race、anyというメソッドがあり、これらは全て並列実行を実現します。
もちろん、複数のAPIからデータを取得したり、時間がかかる処理を同時に走らせたりする際、Promise.allは非常に便利です。
しかし、「一部の処理が失敗しても結果は全て知りたい」「最も速い結果だけを採用したい」といった、Promise.allでは対応できない複雑なニーズに直面することもあります。
この記事では、Promise.allではできない非同期処理の並列実行を可能にする、allSettled、race、any特性や違いをまとめています。
そもそもPromiseや非同期処理とは何か?については下記記事をご参考ください。
> Promiseや非同期処理とは何か?同期処理との違い(then, catch,finallyとは何か)を実例でわかりやすく解説
> async/awaitとは何か?使い方や注意点を実例で解説(try, catch, finally,逐次実行と並列実行の違い)
Promise.all, allSettled, race, anyの違い
Promiseのメソッドであるall、allSettled、race、anyはいずれも、複数の非同期処理を並列で実行するために使います。
「いつ完了とみなすか」と「失敗が起きた時にどう振る舞うか」が異なります。
| メソッド | 完了条件 | 失敗の振る舞い | ユースケース |
| 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)); 


