JavaScriptで複数の非同期処理をまとめて管理する方法を完全解説!Promise.all初心者ガイド
生徒
「先生、JavaScriptで複数の処理を同時に実行して、全部終わったら結果をまとめて使いたいんですけど、どうしたらいいですか?」
先生
「それならPromise.allを使うと便利です。非同期処理を配列でまとめて渡すと、すべての処理が完了したときに結果をまとめて受け取れます。」
生徒
「非同期処理って何ですか?初めて聞きました。」
先生
「非同期処理とは、時間のかかる処理を待たずに次の処理を進める仕組みです。例えば、ネットからデータを取得するときに、その間プログラムが止まらずに他の処理を実行できるんです。」
生徒
「なるほど、それなら複数のネット通信をまとめて待つこともできるんですね。」
先生
「そうです。では、具体的に使い方を順番に見ていきましょう。」
1. Promise.allとは?
JavaScriptのPromise.allは、複数のPromise(非同期処理の結果を表すオブジェクト)をまとめて管理するためのメソッドです。配列に非同期処理を入れると、すべての処理が完了するまで待って、完了後に結果を配列で返してくれます。失敗した場合は最初に失敗したPromiseのエラーを返します。
2. 基本的なPromise.allの使い方
例えば、3つの非同期処理を同時に実行して、すべての結果をまとめて取得したい場合は以下のように書けます。
const promise1 = new Promise((resolve) => setTimeout(() => resolve("結果1"), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve("結果2"), 2000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve("結果3"), 1500));
Promise.all([promise1, promise2, promise3]).then((results) => {
console.log(results); // ["結果1", "結果2", "結果3"]
});
この例では、3つのPromiseが同時に実行され、すべて完了したときにresults配列に結果がまとめて入ります。
3. 非同期処理の失敗を扱う方法
Promise.allは一つでも失敗したPromiseがあると全体が失敗扱いになります。エラーをキャッチするにはcatchを使います。
const p1 = Promise.resolve("成功1");
const p2 = Promise.reject("失敗2");
const p3 = Promise.resolve("成功3");
Promise.all([p1, p2, p3])
.then((results) => {
console.log(results);
})
.catch((error) => {
console.log("エラーが発生しました:", error); // エラーが発生しました: 失敗2
});
一つでも失敗があるとcatchでエラーが受け取れます。
4. async/awaitとPromise.allを組み合わせる方法
最近のJavaScriptでは、async/awaitと組み合わせて非同期処理を直感的に書くこともできます。以下の例を見てみましょう。
async function fetchAllData() {
const promise1 = new Promise((resolve) => setTimeout(() => resolve("データ1"), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve("データ2"), 2000));
try {
const results = await Promise.all([promise1, promise2]);
console.log(results); // ["データ1", "データ2"]
} catch (error) {
console.log("エラーが発生しました:", error);
}
}
fetchAllData();
async関数内でawait Promise.all([...])と書くことで、すべての非同期処理が終わるまで待機し、完了後に結果を受け取れます。
5. 複数のAPI通信を同時に処理する例
Webアプリでは、複数のAPIからデータを取得して表示する場合があります。Promise.allを使うと効率的です。
async function fetchUsersAndPosts() {
const usersPromise = fetch("https://jsonplaceholder.typicode.com/users").then(res => res.json());
const postsPromise = fetch("https://jsonplaceholder.typicode.com/posts").then(res => res.json());
const [users, posts] = await Promise.all([usersPromise, postsPromise]);
console.log("ユーザー情報:", users);
console.log("投稿情報:", posts);
}
fetchUsersAndPosts();
この例では、ユーザー情報と投稿情報を同時に取得し、両方揃ったタイミングで処理を続けています。
6. 個別の非同期処理が失敗しても進めたい場合
場合によっては、一つの処理が失敗しても他の結果を使いたいことがあります。そのときはcatchで個別にエラー処理をして配列に渡します。
const tasks = [
fetch("https://jsonplaceholder.typicode.com/users").then(res => res.json()).catch(() => null),
fetch("https://jsonplaceholder.typicode.com/invalid-url").then(res => res.json()).catch(() => null),
];
Promise.all(tasks).then((results) => {
console.log(results); // 成功したデータは配列に入り、失敗したデータはnullになる
});
これにより、一部の非同期処理が失敗しても他の結果を利用できます。
7. 実務での使い方のポイント
実際のWebアプリ開発では、Promise.allを使って以下のような場面で便利です。
- 複数のAPIから同時にデータを取得する
- 画像やファイルを並行アップロードする
- ユーザー操作に応じて複数の非同期処理をまとめて待つ
重要なのは、処理が全て成功した場合と、一部失敗した場合の扱いをあらかじめ設計しておくことです。
8. まとめの前に覚えておきたい注意点
Promise.allは全てのPromiseが並行して実行されるため、個々の処理の完了順序に依存せず、結果は配列の順序通りに返されます。また、処理が多すぎる場合は同時実行による負荷に注意してください。
このように、Promise.allを使いこなすことで、複数の非同期処理を効率的にまとめて管理できるようになります。
まとめ
本記事では、JavaScriptにおける複数の非同期処理をまとめて管理するためのPromise.allの基本概念と実践的な使い方について詳しく解説しました。非同期処理はWebアプリやNode.jsの開発において非常に重要な要素であり、複数のAPI通信やファイル操作、タイマー処理などを同時に行いたい場合に威力を発揮します。Promise.allを利用することで、複数の非同期処理がすべて完了するまで待ち、結果を配列としてまとめて取得できます。また、async/awaitと組み合わせることで、直感的に同期処理のようなコードで非同期処理を扱うことが可能になります。
さらに、非同期処理の一部が失敗するケースについても触れました。一つのPromiseが失敗すると全体が失敗扱いになる標準の挙動を理解し、個別にcatchでエラー処理を行いながら、他の結果を利用できる工夫も紹介しました。これにより、部分的に失敗してもアプリ全体の処理を止めずに柔軟に対応できます。また、Promise.allを利用する際の注意点として、処理が大量にある場合の同時実行による負荷や、結果の配列が順序通りで返されることなども確認しておくと安心です。
実務では、複数APIの同時取得、画像やファイルの並行アップロード、ユーザー操作に応じた非同期処理のまとめ待ちなど、多様な場面でPromise.allが活躍します。Promise.allを適切に活用することで、コードの可読性と効率性を向上させるだけでなく、Webアプリのパフォーマンス向上にもつながります。特にasync/awaitと組み合わせた実装は、エラー処理も含めて非常に扱いやすく、モダンなJavaScript開発の現場で推奨される方法です。
サンプルまとめコード
async function loadAllResources() {
const api1 = fetch("https://jsonplaceholder.typicode.com/users").then(res => res.json()).catch(() => null);
const api2 = fetch("https://jsonplaceholder.typicode.com/posts").then(res => res.json()).catch(() => null);
const api3 = fetch("https://jsonplaceholder.typicode.com/comments").then(res => res.json()).catch(() => null);
try {
const [users, posts, comments] = await Promise.all([api1, api2, api3]);
console.log("ユーザー:", users);
console.log("投稿:", posts);
console.log("コメント:", comments);
} catch (error) {
console.log("全体の処理でエラーが発生:", error);
}
}
loadAllResources();
この例では、複数のAPI通信を同時に行い、失敗した場合も個別にnullで処理する方法を示しています。Promise.allを使うことで、全体が効率よく完了するのを待ちつつ、部分的な失敗にも対応できます。
生徒
「先生、Promise.allを使えば、複数の非同期処理を一気に管理できることがよくわかりました。でも、失敗したときの挙動が少し怖いです。」
先生
「そうですね、通常は一つでも失敗すると全体が失敗になります。ですが、個別にcatchでエラー処理をすれば、部分的に失敗しても他の結果を利用できます。例えば、API通信が一部失敗しても、成功したデータだけでアプリを動かすことができます。」
生徒
「なるほど、そうすると実務でも安心して使えそうです。async/awaitと組み合わせると、コードもすっきりしますね。」
先生
「その通りです。Promise.allとasync/awaitを組み合わせることで、複雑な非同期処理も見やすく、管理しやすくなります。複数のAPI通信やファイル操作を効率的に行うために、ぜひ活用してください。」
生徒
「分かりました。Promise.allを使いこなせば、Webアプリのパフォーマンスも改善できるんですね。」
先生
「その通りです。処理の並行実行によって待ち時間を減らせるため、ユーザー体験も向上します。順序や失敗への対応を意識しながら、効率よく非同期処理を設計していきましょう。」