JavaScriptの非同期処理は、現代のWeb開発において欠かせない技術です。本記事では、Promise・async/awaitの基礎から実践的な活用方法まで徹底解説します。APIリクエスト、ファイル操作、並列処理など実際のユースケースを通じて、あなたのJavaScriptスキルを次のレベルへ引き上げましょう。
JavaScriptの非同期処理とは何か
JavaScriptはシングルスレッドで動作する言語です。つまり、一度に1つの処理しか実行できません。しかし、Webアプリケーションでは、APIからのデータ取得やファイルの読み込みなど、時間のかかる処理を効率よく扱う必要があります。これを解決するのが非同期処理です。
非同期処理により、時間のかかる処理をバックグラウンドで実行しながら、メインスレッドの処理を続けることができます。従来のコールバック関数から進化したPromise、そしてasync/awaitが現代のJavaScript開発の標準となっています。
Promiseの基礎を理解する
Promiseは非同期処理の「約束」を表すオブジェクトです。3つの状態があります:pending(待機中)、fulfilled(成功)、rejected(失敗)。
// Promiseの基本的な作り方
const fetchData = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve({ data: 'ユーザー情報取得成功', userId: 123 });
} else {
reject(new Error('データ取得に失敗しました'));
}
}, 1000);
});
fetchData
.then(result => console.log('成功:', result.data))
.catch(error => console.error('エラー:', error.message))
.finally(() => console.log('処理完了'));
async/awaitで読みやすいコードを書く
async/awaitはPromiseをより直感的に書くための構文糖衣(シンタックスシュガー)です。非同期コードを同期コードのように記述できるため、可読性が大幅に向上します。
async function getUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) throw new Error(`HTTPエラー: ${response.status}`);
const user = await response.json();
return user;
} catch (error) {
console.error('ユーザーデータの取得に失敗:', error.message);
throw error;
}
}
Promise.allで並列処理を最適化する
複数の非同期処理を同時に実行したい場合、Promise.all()が非常に有効です。順番に処理するより大幅に高速化できます。
// 並列処理(速い)
async function fetchParallel() {
const [user, posts, comments] = await Promise.all([
fetchUser(1),
fetchPosts(1),
fetchComments(1)
]);
return { user, posts, comments };
}
// Promise.allSettledで一部失敗してもOK
const results = await Promise.allSettled([
fetchUser(1),
fetchPosts(1),
fetchComments(999) // エラーになるが全体は続行
]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.log('失敗:', result.reason);
}
});
エラーハンドリングのベストプラクティス
非同期処理のエラーハンドリングは、アプリケーションの安定性に直結します。カスタムエラークラスとtry/catchを適切に組み合わせましょう。
class ApiError extends Error {
constructor(message, statusCode) {
super(message);
this.name = 'ApiError';
this.statusCode = statusCode;
}
}
async function fetchUserSafely(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (response.status === 404) throw new ApiError('ユーザーが見つかりません', 404);
if (!response.ok) throw new ApiError('サーバーエラー', response.status);
return await response.json();
} catch (error) {
if (error instanceof ApiError) {
console.error(`APIエラー [${error.statusCode}]: ${error.message}`);
} else if (error instanceof TypeError) {
console.error('ネットワーク接続を確認してください');
} else {
console.error('予期しないエラー:', error);
}
return null;
}
}
まとめ:非同期処理マスターへのロードマップ
- Step 1:コールバック関数の仕組みを理解する
- Step 2:Promiseの3つの状態(pending/fulfilled/rejected)を理解する
- Step 3:.then()/.catch()/.finally()でPromiseチェーンを書く
- Step 4:async/awaitで同期的に見えるコードを書く
- Step 5:Promise.all()で並列処理を最適化する
- Step 6:TypeScriptと組み合わせて型安全な非同期処理を書く
非同期処理の理解は、モダンJavaScript開発の核心です。ReactやVue.jsなどのフレームワークを使う際も、この知識は必ず役立ちます。ぜひ実際にコードを書いて、非同期処理をマスターしてください。