Promise.race を一言でいうと
Promise.race は、
「複数の Promise のうち、“一番早く決着したやつだけ” の結果を採用する関数」 です。
race(レース)という名前の通り、
ゴールに一番最初に到着した Promise が「勝ち」で、
その成功 or 失敗が Promise.race 全体の結果になります。
ここが重要です。Promise.all は「全員がゴールするまで待つ」Promise.race は「最初にゴールした 1 人だけを見る」
というイメージで区別すると、直感的に理解しやすくなります。
基本の形と動き方
どう呼ぶか(シグネチャ)
形は Promise.all や allSettled と同じく、
「Promise の配列」を渡します。
Promise.race([promise1, promise2, promise3])
.then((value) => {
// 最初に「成功」した Promise の値
})
.catch((error) => {
// 最初に「失敗」した Promise のエラー
});
JavaScript動きはこうです。
一番最初に「fulfilled(成功)」または「rejected(失敗)」になった Promise の結果だけを採用し、
他の Promise の結果は 無視 します。
ここがポイントです。
「最初に成功したもの」だけではなく、「最初に失敗したもの」も勝ちます。
とにかく“一番早く settled(決着がついた)もの” が勝者になるレースです。
ごく簡単な例
const p1 = new Promise((resolve) => {
setTimeout(() => resolve("p1"), 1000); // 1秒後
});
const p2 = new Promise((resolve) => {
setTimeout(() => resolve("p2"), 500); // 0.5秒後
});
Promise.race([p1, p2]).then((value) => {
console.log("勝ったのは:", value); // 勝ったのは: p2
});
JavaScriptこの場合、
p1 → 1秒後に “p1” で成功
p2 → 0.5秒後に “p2” で成功
なので、p2 がレースに勝ち、Promise.race 全体の結果は "p2" になります。
成功が勝つ場合と、失敗が勝つ場合
一番早く「成功」したケース
まずは「成功同士の勝負」から。
const fast = new Promise((resolve) => {
setTimeout(() => resolve("fast"), 300); // 0.3秒
});
const slow = new Promise((resolve) => {
setTimeout(() => resolve("slow"), 1000); // 1秒
});
Promise.race([fast, slow]).then((value) => {
console.log("結果:", value); // 結果: fast
});
JavaScript0.3 秒の fast が先に成功するので、then の value は "fast" になります。
1 秒後に来る slow はもう無視です。
一番早く「失敗」したケース
次に、失敗が勝つパターン。
const ok = new Promise((resolve) => {
setTimeout(() => resolve("OK"), 1000);
});
const fail = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("早いエラー")), 300);
});
Promise.race([ok, fail])
.then((value) => {
console.log("成功:", value);
})
.catch((err) => {
console.log("失敗:", err.message); // 失敗: 早いエラー
});
JavaScript300ms 後に fail がエラーになるので、Promise.race は rejected と判断され、catch に "早いエラー" が渡されます。
このとき、
1 秒後に来るはずだった "OK" の結果は、
すでにレースが終わっているので採用されません。
ここが重要です。Promise.race は「早い者勝ち」。
その「早い者」が成功か失敗かは関係なく、その結果だけが then/catch に届きます。
何に使うのか:典型パターンは「タイムアウト」
「一定時間待っても終わらないなら諦めたい」
Promise.race が特に力を発揮するのは、
「タイムアウト(時間制限)」を実装したいとき です。
イメージとしては、
サーバーからデータを取得する Promise
一定時間待っても終わらない場合に reject される「タイマー Promise」
この 2 つを Promise.race にかけて、
どちらが先に終わるかで挙動を変えます。
タイマー Promise を作る
まずは「n ミリ秒後に失敗する Promise」を作ります。
function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`${ms}ms タイムアウト`));
}, ms);
});
}
JavaScripttimeout(3000) は「3 秒待っても何もなければエラーになる Promise」です。
通信+タイムアウトを race させる
たとえば fetch で通信する場合(イメージです)。
function fetchWithTimeout(url, ms) {
return Promise.race([
fetch(url), // 通信の Promise
timeout(ms), // タイムアウトの Promise
]);
}
JavaScript使い方はこうです。
fetchWithTimeout("/api/data", 3000)
.then((response) => {
console.log("成功:", response);
})
.catch((err) => {
console.error("失敗:", err.message);
});
JavaScriptここでの挙動は次のようになります。
ネットワークが速く、3 秒以内に fetch(url) が成功する
→ Promise.race は fetch の結果を採用し、then が呼ばれる
サーバーが遅くて 3 秒を超える
→ timeout(3000) のほうが先に reject される
→ Promise.race はタイムアウトエラーを採用し、catch が呼ばれる
ここが重要です。Promise.race を使うと、「どちらが先か分からない 2 つ(または複数)の処理」のうち、“早く決着した方の結果だけを使う” という制御が簡単に書けるようになります。
複数の候補から「最初の成功」を採用したい場合
単純な race だと「早いエラー」が勝ってしまう
「複数のサーバーに同じリクエストを投げて、
どれか一つ成功したらその結果を使いたい」
というようなケースを考えてみましょう。
素直に Promise.race すると、
一番早いのが「失敗」だった場合、そのエラーで終わってしまう
という問題があります。
Promise.race([server1(), server2(), server3()])
.then((res) => {
console.log("成功:", res);
})
.catch((err) => {
console.error("失敗:", err);
});
JavaScriptもし server1 が一番早く「エラー」になった場合、
server2, server3 が後から成功しても、
race 全体はすでに「失敗」で終わっています。
「最初の成功」だけ欲しいなら、工夫が必要になる
「最初に settled(成功 or 失敗)したもの」ではなく、
「最初に成功したもの」 を採用したい場合は、Promise.any(ES2021)という別のメソッドを使うか、
自前で工夫する必要があります。
ここでは詳細には踏み込みませんが、race の性質として
「早い失敗も勝者になってしまう」
という点は覚えておいてください。
ここが重要です。Promise.race=「最初に決着した 1 つの結果」であり、
「最初に成功した結果」ではない。
この違いを意識して使いどころを考えるのが大切です。
ちょっとした注意点とイメージの整理
他の Promise は「勝敗には関係なくなる」が、実行自体は止まらない
Promise.race は、一番早いやつを「結果として採用する」だけで、
残りの Promise を 強制停止するわけではありません。
例えば、さっきの例:
const p1 = new Promise((resolve) => {
setTimeout(() => {
console.log("p1 実行完了");
resolve("p1");
}, 1000);
});
const p2 = new Promise((resolve) => {
setTimeout(() => {
console.log("p2 実行完了");
resolve("p2");
}, 500);
});
Promise.race([p1, p2]).then((value) => {
console.log("race 結果:", value);
});
JavaScript実行順は、
0.5秒後: “p2 実行完了” → race の勝者として "p2" が採用される
1.0秒後: “p1 実行完了”(ログは出るが、race の結果には影響しない)
のようになります。
つまり、
race が決着しても、他の非同期処理は裏で普通に動き続ける
ということです。
「本当に途中で止めたい」のなら、
AbortController や独自のキャンセル機構など、別の仕組みが必要になります。
配列の中身は Promise じゃなくてもよい
Promise.all や allSettled と同様に、race に渡す配列の中には「普通の値」も入れられます。
普通の値は「すぐに成功した Promise」として扱われるため、
ほぼ確実に勝ってしまいます。
Promise.race([
123,
new Promise((resolve) => setTimeout(() => resolve("遅い"), 1000)),
]).then((value) => {
console.log(value); // 123
});
JavaScriptこのように使うことはあまり多くありませんが、
「中身は全部 Promise でなければいけないわけではない」という柔軟性はあります。
初心者としての「Promise.race」の押さえどころ
最後に、本当に大事なポイントだけを整理します。
Promise.race([p1, p2, p3]) は、「p1, p2, p3 のうち、一番早く settled(成功 or 失敗)したものの結果だけを採用する」関数。
一番先に成功したら → then が呼ばれ、その値が渡る。
一番先に失敗したら → catch が呼ばれ、そのエラーが渡る。
典型的な用途は「タイムアウトの実装」:
「本物の処理」 vs 「時間切れエラー Promise」を race させて、早い方を採用する。
他の Promise は race の結果には使われなくなるが、実行自体は裏で続いている点に注意。
Promise.all は「全員ゴールを待つ」、Promise.race は「誰か1人がゴールした瞬間にレース終了」。
ここが重要です。Promise.race を「早い者勝ちルールを作るための道具」として捉えておくと、
「どっちが先に終わるか分からない処理」を扱うときに、とても強力な武器になります。
練習としては、
1秒後に resolve する Promise と、300ms 後に reject する Promise を用意してPromise.race にかけてみる
それを自分で動かしてみて、
「早い失敗が勝って catch に来る」動きを目で確認すると、Promise.race のイメージがしっかり定着するはずです。
