JavaScriptのエラー処理の基本
エラーは「プログラムが思ってない動きをしたときに出るサイン」。怖がるより、どう受け止めて次に進むかが大事です。ここでは、初心者にもわかるように、try...catchでエラーを受け止める方法を、例題つきで丁寧に説明します。
エラーを受け止める仕組み(try…catch)
- 役割:
tryの中でコードを実行し、エラーが起きたらcatchがそれを受け取って処理します。 - 最小例:
try {
// ここでエラーになる(存在しない関数を呼び出し)
notDefinedFunction();
console.log("ここは実行されません");
} catch (error) {
console.log("エラーをキャッチしました:", error.message);
}
console.log("プログラムは止まらずに続行します");
JavaScript- ポイント:
- エラーが出てもプログラム全体を止めないで、次の処理に進める。
error.messageには読みやすい説明が入ります。必要ならerror.nameやerror.stackも使えます。
関数の中のエラーは呼び出し元でキャッチできる(同期処理)
- イメージ:
関数の中で起きた事故も、外側のtry...catchで受け止められる。 - 例題1:型の組み合わせミス(NumberとBigInt)
function add(a, b) {
// ここでエラーが発生(NumberとBigIntは直接足せない)
return a + b;
}
try {
console.log(add(10, 8)); // 18(OK)
console.log(add(10, 8n)); // ここでエラー
} catch (error) {
console.log("キャッチ:", error.message); // e.g. "Cannot mix BigInt and other types"
}
console.log("続行可能");
JavaScript- 例題2:入れ子の関数でもOK
function calcTotal(a, b) {
return addStrict(a, b);
}
function addStrict(a, b) {
if (typeof a !== "number" || typeof b !== "number") {
throw new Error("数値だけを足してください");
}
return a + b;
}
try {
console.log(calcTotal(5, 7)); // 12(OK)
console.log(calcTotal(5, "7")); // ここでErrorをthrow → catchへ
} catch (error) {
console.log("キャッチ:", error.message); // "数値だけを足してください"
}
JavaScript- ポイント:
- 同期的(その場で直ちに実行)される関数の中のエラーは、呼び出し元の
catchで受け止められる。 - エラーを自分で出したいときは
throw new Error("メッセージ")を使う。
- 同期的(その場で直ちに実行)される関数の中のエラーは、呼び出し元の
非同期処理の落とし穴(外側のtry…catchでは拾えない)
- イメージ:
setTimeoutやイベントのコールバックは、あとで別タイミングで実行されます。外側のtry...catchは、その「あとで」のエラーを見張れません。 - 悪い例(キャッチされない):
try {
setTimeout(() => {
// ここでエラーが起きても、外側のcatchには届かない
console.log(10 + 8n);
}, 1000);
} catch (error) {
console.log("これは呼ばれない");
}
JavaScript- 正しい例(コールバック内でキャッチする):
setTimeout(() => {
try {
console.log(10 + 8n); // エラー発生
} catch (error) {
console.log("コールバック内でキャッチ:", error.message);
}
}, 1000);
JavaScript- ポイント:
- 非同期のエラーは、その非同期の中(コールバックやPromiseチェーン)でキャッチする。
- 外側の
try...catchで包んでも意味がないことが多い。
非同期の正しいエラー処理(Promise/async)
- Promiseの
.catchを使う:
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// 失敗した想定
reject(new Error("通信に失敗しました"));
}, 500);
});
}
fetchData()
.then((data) => {
console.log("成功:", data);
})
.catch((error) => {
console.log("キャッチ:", error.message);
});
JavaScript- async/awaitとtry…catchを組み合わせる:
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("通信に失敗")), 500);
});
}
async function main() {
try {
const data = await fetchData(); // rejectされるとcatchへ
console.log("成功:", data);
} catch (error) {
console.log("キャッチ:", error.message);
} finally {
console.log("片付け処理(必ず実行)");
}
}
main();
JavaScript- ポイント:
- Promise: 失敗は
reject、受け止めは.catch。 - async/await: 非同期でも
try...catchが使える。awaitのところで失敗したらcatchに流れる。 - finally: 成否に関係なく片付けしたい処理を入れる。
- Promise: 失敗は
よくあるミスと実践のコツ
- ミス1:非同期のエラーを外側でキャッチしようとする
- 対策: コールバックの中、またはPromise/asyncの中でキャッチする。
- ミス2:雑なcatchで原因がわからない
- 対策:
- ログを丁寧に:
console.error(error.name, error.message); - 必要なら再throw: ここでは握りつぶさない、上位に任せたいときは
throw。
- ログを丁寧に:
- 対策:
- ミス3:型を混ぜる(NumberとBigIntなど)
- 対策: 入力チェックをして、想定外を
throwする。
- 対策: 入力チェックをして、想定外を
function safeAdd(a, b) {
if (typeof a !== "number" || typeof b !== "number") {
throw new TypeError("number同士のみ加算できます");
}
return a + b;
}
JavaScript- 小さく試す:
問題が起きそうな箇所だけをtry...catchで囲んで、ログを出す。広く囲いすぎると原因追跡が難しくなる。
練習問題(手を動かして理解する)
- 問題1:同期関数のエラー
- 課題:
divide(a, b)を作り、bが0ならError("ゼロでは割れません")を投げる。外側でキャッチしてメッセージを表示。 - ヒント:
- 課題:
function divide(a, b) {
if (b === 0) throw new Error("ゼロでは割れません");
return a / b;
}
try {
console.log(divide(10, 0));
} catch (e) {
console.log("キャッチ:", e.message);
}
JavaScript- 問題2:非同期のエラー(Promise)
- 課題:
getUser(id)が存在しないIDならreject。.catchでメッセージを表示。
- 課題:
function getUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
id === 1 ? resolve({ id: 1, name: "Alice" })
: reject(new Error("ユーザーが見つかりません"));
}, 300);
});
}
getUser(2)
.then(user => console.log("成功:", user))
.catch(err => console.log("キャッチ:", err.message));
JavaScript- 問題3:async/await+finally
- 課題:
loadConfig()をasyncで呼び出し、失敗時にcatch、最後にfinallyで「終了」を表示。
- 課題:
function loadConfig() {
return Promise.reject(new Error("設定ファイルが壊れています"));
}
async function boot() {
try {
const cfg = await loadConfig();
console.log("成功:", cfg);
} catch (e) {
console.log("キャッチ:", e.message);
} finally {
console.log("終了");
}
}
boot();
JavaScript
