【JS応用|実務向け】Promise.allを使いこなして、非同期処理のパフォーマンスを劇的に改善する方法

タイトル: Promise.allを使いこなして、非同期処理のパフォーマンスを劇的に改善する方法

はじめに

Webアプリケーション開発において、非同期処理は避けては通れない道です。特に、複数のAPIリクエストを同時に実行し、その結果をまとめて処理したい場面は頻繁に遭遇します。そんな時に大活躍するのが `Promise.all` です。しかし、単に `Promise.all` を使うだけでなく、その特性を理解し、適切に活用することで、アプリケーションのパフォーマンスを劇的に改善することができます。

この記事では、実務で役立つ `Promise.all` の使い方、注意点、そしてより高度な活用方法について、具体的なコード例を交えながら解説していきます。

Promise.allの基本

`Promise.all` は、複数のPromiseオブジェクトを引数に取り、それらすべてのPromiseがfulfilled(成功)したときに、それぞれのfulfilledされた値の配列を返す新しいPromiseを生成します。もし、引数に渡されたPromiseのいずれかがrejected(失敗)した場合、`Promise.all` はそのrejectされた理由をそのままrejectします。

// 2つの非同期処理を同時に実行し、両方が完了したら結果を表示
const promise1 = new Promise((resolve) => setTimeout(() => resolve(‘結果1’), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(‘結果2’), 500));

Promise.all([promise1, promise2])
.then((results) => {
console.log(results); // [‘結果1’, ‘結果2’] が表示される (promise1の完了を待ってから)
})
.catch((error) => {
console.error(‘エラーが発生しました:’, error);
});

この例では、`promise1` が1秒、`promise2` が0.5秒かかります。`Promise.all` は、すべてのPromiseが完了するのを待つため、結果が表示されるのは約1秒後になります。重要なのは、`promise1` と `promise2` が同時に実行されている点です。これにより、逐次処理に比べて待機時間を大幅に削減できます。

失敗時の挙動と注意点

`Promise.all` の特性として、いずれかのPromiseがrejectされると、`Promise.all` 全体がrejectされることを理解しておく必要があります。

const successPromise = new Promise((resolve) => setTimeout(() => resolve(‘成功’), 1000));
const failurePromise = new Promise((_, reject) => setTimeout(() => reject(new Error(‘失敗’)), 500));

Promise.all([successPromise, failurePromise])
.then((results) => {
console.log(results);
})
.catch((error) => {
console.error(‘エラー:’, error.message); // ‘失敗’ が表示される
});

この場合、`failurePromise` が先にrejectされるため、`Promise.all` はその時点でrejectされ、`successPromise` の結果は取得できません。

実務での注意点:

  • エラーハンドリングの重要性: 複数の非同期処理を行う場合、いずれかが失敗する可能性は常にあります。`catch` ブロックで適切にエラーをハンドリングし、ユーザーに分かりやすいメッセージを表示したり、代替処理を行ったりすることが不可欠です。
  • リトライ処理: ネットワークエラーなど、一時的な失敗であればリトライすることで成功する可能性があります。`Promise.all` の外側でリトライロジックを実装するか、Promiseライブラリ(例: `axios` には`retry`オプションがあります)を活用することを検討しましょう。

Promise.allSettledの活用

「すべてのPromiseが成功しなくても、個々の結果を知りたい」という場面では、`Promise.allSettled` が非常に有用です。`Promise.allSettled` は、引数に渡されたすべてのPromiseがsettled(fulfilledまたはrejected)した後に、各Promiseの結果(ステータスと値/理由)を含むオブジェクトの配列を返します。

const promiseA = new Promise((resolve) => setTimeout(() => resolve(‘A 成功’), 1000));
const promiseB = new Promise((_, reject) => setTimeout(() => reject(new Error(‘B 失敗’)), 500));

Promise.allSettled([promiseA, promiseB])
.then((results) => {
console.log(results);
/
[
{ status: ‘fulfilled’, value: ‘A 成功’ },
{ status: ‘rejected’, reason: Error: B 失敗 }
]
/
results.forEach((result) => {
if (result.status === ‘fulfilled’) {
console.log(‘成功:’, result.value);
} else {
console.error(‘失敗:’, result.reason.message);
}
});
});

`Promise.allSettled` を使うことで、一部の非同期処理が失敗しても、全体の処理を続行し、成功した処理の結果を活かすことができます。これは、例えば複数の外部APIからデータを取得し、一部のAPIがダウンしていても、取得できたデータだけでも表示したい場合などに有効です。

複数のAPIリクエストを効率化する具体例

例えば、ユーザーの詳細情報と、そのユーザーに関連する投稿リストを同時に取得したいとします。

async function fetchUserData(userId) {
const userResponse = await fetch(`/api/users/${userId}`);
if (!userResponse.ok) {
throw new Error(`ユーザー情報の取得に失敗しました: ${userResponse.statusText}`);
}
return userResponse.json();
}

async function fetchUserPosts(userId) {
const postsResponse = await fetch(`/api/users/${userId}/posts`);
if (!postsResponse.ok) {
throw new Error(`投稿リストの取得に失敗しました: ${postsResponse.statusText}`);
}
return postsResponse.json();
}

async function displayUserProfile(userId) {
try {
// 2つのAPIリクエストを同時に実行
const [userData, postsData] = await Promise.all([
fetchUserData(userId),
fetchUserPosts(userId)
]);

// 取得したデータを元にUIを更新
console.log(‘ユーザー情報:’, userData);
console.log(‘投稿リスト:’, postsData);
// … UI描画処理 …

} catch (error) {
console.error(‘ユーザープロフィールの表示に失敗しました:’, error);
// … エラー表示処理 …
}
}

displayUserProfile(123);

このコードでは、`fetchUserData` と `fetchUserPosts` という2つの非同期関数を `Promise.all` で同時に実行しています。これにより、ユーザー情報と投稿リストの取得にかかる時間を、それぞれのAPIリクエストの合計時間ではなく、より時間のかかる方のリクエストの時間に近づけることができます。

まとめ

`Promise.all` は、複数の非同期処理を効率的に実行するための強力なツールです。

  • `Promise.all`: すべてのPromiseが成功した場合にのみ成功し、いずれかが失敗すると全体が失敗します。
  • `Promise.allSettled`: すべてのPromiseがsettled(成功または失敗)した後に、個々の結果を配列で返します。

これらのAPIを理解し、エラーハンドリングを適切に行うことで、より高速で堅牢なWebアプリケーションを開発することができます。ぜひ、あなたのプロジェクトで `Promise.all` を積極的に活用してみてください。

タイトルとURLをコピーしました