導入
フロントエンド開発において、複数のAPIからデータを取得する際、「どれか一つでも成功すればすぐに処理を進めたい」というケースはありませんか?例えば、複数のCDNから同じリソースを読み込んだり、複数の検索エンジンから結果を統合したりする場合です。このような場面で、従来はPromise.raceを使用していましたが、これは「拒否(reject)」も拾ってしまうという課題がありました。本記事で解説する Promise.any は、複数の非同期処理のうち「最初に成功したもの」だけを採用できるため、耐障害性の高いアプリケーション構築に不可欠な技術です。
基礎知識
Promise.any は、渡されたイテラブル(配列など)の中に含まれるPromiseのうち、どれか一つが最初に「解決(fulfilled)」された瞬間に、その値を返して終了するメソッドです。
重要なのは、他のPromiseが拒否(rejected)されても無視されるという点です。もし全てのPromiseが失敗した場合のみ、AggregateErrorという特別なエラーが投げられます。これは、Promise.raceが「最初に決着がついたもの(成功・失敗問わず)」を返すのとは明確に異なる挙動です。
実装/解決策
Promise.anyを使うことで、フォールバック戦略を簡潔に実装できます。例えば、メインのAPIサーバーがダウンしている場合に、バックアップとして別のサーバーへリクエストを飛ばすといった処理を、複雑な条件分岐を書くことなく実現できます。エラーハンドリングは、全ての試行が失敗した場合のみcatchブロックで行えば良いため、コードの見通しが非常に良くなります。
サンプルプログラム
以下のコードは、3つの異なるサーバーからデータを取得しようとし、最初に成功したレスポンスのみを表示する例です。
// サーバーからのレスポンスをシミュレートする関数
const fetchFromSource = (name, delay, shouldFail = false) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail) {
reject(`${name} でエラーが発生しました`);
} else {
resolve(`${name} からのデータ`);
}
}, delay);
});
};
const main = async () => {
try {
const promises = [
fetchFromSource('サーバーA', 2000, true), // 2秒後に失敗
fetchFromSource('サーバーB', 500), // 0.5秒後に成功
fetchFromSource('サーバーC', 1000) // 1秒後に成功
];
// 最初に成功したPromiseの結果を待つ
const result = await Promise.any(promises);
console.log('取得成功:', result); // サーバーB からのデータ が出力される
} catch (error) {
// 全てのPromiseが失敗した場合のみここが実行される
console.error('全てのソースから取得できませんでした:', error.errors);
}
};
main();
応用・注意点
現場で活用する際の注意点は、ブラウザの対応状況です。Promise.anyは比較的新しい仕様であるため、古いブラウザ(IEなど)をサポートする必要がある場合は、core-jsなどのポリフィルを導入してください。
また、開発中は「全て失敗した場合」のテストを忘れずに行ってください。AggregateErrorオブジェクトには errorsプロパティ があり、そこに各Promiseが拒否された際のエラー内容が配列で格納されています。これを確認することで、どのリクエストが失敗したのかをデバッグ時に追跡しやすくなります。ネットワークが不安定な環境や、冗長性を持たせたいマイクロフロントエンド構成において、ぜひ活用してみてください。