JavaScriptの非同期処理(Promise)のエラーハンドリングを初心者向けに徹底解説
生徒
「先生、JavaScriptでネットワーク通信とか時間がかかる処理をするとき、どうやってエラーを安全に扱えばいいんですか?」
先生
「その場合には、Promiseという仕組みを使って非同期処理を扱い、catchやtry...catchでエラーを適切に処理する方法があります。」
生徒
「Promiseって何ですか?難しそうです。」
先生
「簡単に言うと、『未来に返ってくる結果を表す箱』です。結果が成功か失敗か分かるまで待つことができます。」
生徒
「なるほど。エラーが起きたときも安全に処理できるんですね!」
1. Promiseとは何か?
JavaScriptのPromiseは、非同期処理の結果を扱うためのオブジェクトです。例えば、サーバーからデータを取得する処理は時間がかかることがあります。その結果をすぐには返せません。Promiseはその「まだ結果が分からない状態」を管理し、処理が成功した場合と失敗した場合の動作を分けて書ける仕組みです。
const promise = new Promise((resolve, reject) => {
const success = true; // 成功フラグ
if (success) {
resolve("データ取得成功");
} else {
reject("データ取得失敗");
}
});
promise.then(result => {
console.log(result);
}).catch(error => {
console.error(error);
});
この例ではresolveが呼ばれるとthen、rejectが呼ばれるとcatchに処理が行きます。
2. thenとcatchでエラー処理する方法
Promiseの基本はthenとcatchです。thenで成功時の処理を書き、catchでエラー時の処理を書きます。順番を間違えるとエラーがキャッチできなくなるので注意が必要です。
fetch("https://example.com/data.json")
.then(response => response.json())
.then(data => {
console.log("取得したデータ:", data);
})
.catch(error => {
console.error("エラーが発生しました:", error);
});
この例ではネットワークの問題やデータの形式が間違っている場合、catchが呼ばれてエラーを安全に処理できます。
3. async/awaitを使ったエラーハンドリング
async/awaitはPromiseをより直感的に扱える書き方です。非同期処理をまるで同期処理のように書けます。エラーはtry...catchで囲むことで安全に処理できます。
async function fetchData() {
try {
const response = await fetch("https://example.com/data.json");
const data = await response.json();
console.log("取得したデータ:", data);
} catch (error) {
console.error("エラーが発生しました:", error);
}
}
fetchData();
この書き方は初心者でも読みやすく、複雑なPromiseチェーンを避けることができます。
4. 複数のPromiseをまとめてエラーハンドリングする方法
複数の非同期処理を同時に実行したい場合はPromise.allを使います。いずれかの処理が失敗した場合もcatchでまとめてエラー処理が可能です。
const promise1 = fetch("https://example.com/data1.json");
const promise2 = fetch("https://example.com/data2.json");
Promise.all([promise1, promise2])
.then(responses => Promise.all(responses.map(r => r.json())))
.then(dataArray => {
console.log("データ1:", dataArray[0]);
console.log("データ2:", dataArray[1]);
})
.catch(error => {
console.error("いずれかの処理でエラー:", error);
});
これで複数の非同期処理も安全に扱えます。
5. Promiseチェーンの途中でのエラー処理
Promiseチェーンでは、途中でエラーが発生すると後続のthenはスキップされ、最初に見つかったcatchに飛びます。
new Promise((resolve, reject) => {
resolve(10);
})
.then(num => {
console.log("最初の処理:", num);
throw new Error("途中でエラー発生");
})
.then(num => {
console.log("この処理は実行されない");
})
.catch(error => {
console.error("キャッチしたエラー:", error);
});
これにより、途中でエラーが発生しても安全に処理を止め、必要なログを出すことができます。
6. 非同期処理でよくあるエラーの例と対策
非同期処理では、ネットワークエラーやタイムアウト、データ形式の不一致などがよく起こります。Promiseやasync/awaitを使った場合、必ずcatchやtry...catchで包むことでエラーを安全に処理できます。
async function getData() {
try {
const response = await fetch("https://example.com/wrong-url.json");
const data = await response.json();
console.log(data);
} catch (error) {
console.error("ネットワークやURLエラーをキャッチしました:", error);
}
}
getData();
このようにしておけば、初心者でも安心して非同期処理を書けます。
7. Promiseのまとめ方と注意点
非同期処理は正しくエラーを扱わないとプログラムが予期せず停止することがあります。Promiseのthen/catchやasync/awaitとtry...catchを使うことで、処理の成功と失敗を安全に管理できます。複数の非同期処理を扱う場合はPromise.allやPromise.raceなども覚えておくと便利です。
Promise.resolve("成功")
.then(result => console.log(result))
.catch(error => console.error(error));
シンプルなPromiseでも、必ずcatchでエラーを処理する癖をつけると、バグが少なくなります。
まとめ
今回の記事では、JavaScriptにおける非同期処理の基本であるPromiseの仕組みから、エラーハンドリングの方法まで、初心者向けに丁寧に解説しました。非同期処理はサーバー通信やタイマー処理、ファイル操作など、時間のかかる処理を扱う際に欠かせない技術です。Promiseは「未来の結果を表す箱」と考えると理解しやすく、thenで成功時の処理、catchで失敗時の処理を書くことができます。また、async/awaitを使うことで、非同期処理を同期処理のように直感的に書くことができ、try...catchで安全にエラー処理が可能です。
複数のPromiseをまとめて処理したい場合はPromise.allを活用することで、いずれかの処理が失敗した場合もまとめてエラーをキャッチできます。さらに、Promiseチェーンでは途中で発生したエラーは後続のthenをスキップし、最初に現れるcatchに飛ぶため、チェーン全体の安全性が確保されます。実務ではネットワークエラーやタイムアウト、データ形式の不一致などがよく起こるため、必ずエラー処理を組み込むことが重要です。
初心者が陥りやすいポイントとして、Promiseチェーンの書き方の順序や、async関数内でのtry...catchの忘れがあります。これを理解しておくことで、予期せぬプログラムの停止やエラーの見逃しを防ぐことができます。今回解説したように、単純なPromiseから複数の非同期処理のまとめ方まで、基礎をしっかり押さえることで、より安全で読みやすいコードを書くことができます。
具体的には、次のようにPromiseやasync/awaitを使った安全な非同期処理が書けます。
async function safeFetch(url) { try { const response = await fetch(url); const data = await response.json(); console.log("取得データ:", data); } catch (error) { console.error("エラーをキャッチしました:", error); } }
const urls = ["https://example.com/data1.json", "https://example.com/data2.json"];
Promise.all(urls.map(u => fetch(u).then(r => r.json())))
.then(results => {
console.log("複数データ取得:", results);
})
.catch(error => {
console.error("複数処理のいずれかでエラー:", error);
});
このように書くことで、単一処理でも複数処理でもエラーに強い安全なコードを作ることができます。非同期処理の正しい理解とエラーハンドリングは、WebアプリケーションやNode.jsなどのサーバーサイド開発においても非常に重要です。
また、Promiseの基本を理解していれば、Promise.raceやPromise.allSettledなど応用的な非同期制御もスムーズに学べます。これにより、処理時間の短いものを優先したり、全ての結果を確認したりと柔軟な非同期処理が可能になります。初心者でも、まずは基本のthen/catchやasync/awaitのtry...catchをしっかり押さえることが、将来的な高度な非同期処理の理解につながります。
生徒
「先生、Promiseの基本やasync/awaitの使い方がだんだん理解できてきました。thenとcatchでエラーを分けて書くことが大事なんですね。」
先生
「そうです。非同期処理ではエラーを適切にキャッチすることが非常に重要です。async/awaitを使うと、try...catchで直感的に書けるので、コードが読みやすくなります。」
生徒
「複数の非同期処理もまとめて扱えるんですね。Promise.allで同時に実行して、どれかが失敗したらcatchで処理できるんですね。」
先生
「その通りです。さらに応用的にPromise.raceやPromise.allSettledも覚えると、処理を最適化したり、全ての結果を確認したりできるようになります。」
生徒
「なるほど。非同期処理の基本から応用まで順番に理解していくと、エラーに強い安全なJavaScriptコードを書けるようになるんですね。」
先生
「そうです。まずはPromiseの基本とasync/awaitのtry...catchをしっかり押さえ、ネットワークエラーやタイムアウト、データ不一致などに対応できるコードを書く練習を続けることが大切です。」