ここでは、「非同期処理(fetch / async)」でよくあるエラーの落とし穴を、
「悪い書き方 → なぜダメか → 良い書き方」に整理して解説します。
初心者がつまずきやすいポイントを中心に、
実際のコードとコメントで具体的に見ていきましょう。
前提:fetch() はエラーを「投げない」ことがある!
まず基本ルールです。
const res = await fetch("https://example.com/api");
JavaScriptこの fetch() は、
- ネットワークに失敗した場合 →
TypeErrorが発生(例:オフライン、DNS失敗など) - HTTPエラー(404, 500 など) → 例外は発生しない!(成功扱い、
res.okが false になるだけ)
つまり、
「fetch に失敗した」の意味が、通信エラーなのか、HTTPエラーなのかで挙動が違う!
❌ 悪い書き方① — await fetch() を try/catch で囲んでるだけ
async function loadData() {
try {
const res = await fetch("https://example.com/api/data");
const data = await res.json();
console.log("成功:", data);
} catch (e) {
console.error("fetchに失敗しました:", e);
}
}
JavaScript🪲 落とし穴:
- サーバーが
404や500を返しても、ここでは catch に入らない。 res.okがfalseのままres.json()へ進んでしまい、
→ JSON が空やHTMLの場合に別のエラー(SyntaxError)が発生。- 結果、「どの段階で失敗したのか」がわからなくなる。
✅ 良い書き方① — res.ok を必ずチェック!
async function loadData() {
try {
const res = await fetch("https://example.com/api/data");
// ✅ HTTPエラー(404など)を自分で検出して throw
if (!res.ok) {
throw new Error(`HTTPエラー: ${res.status}`);
}
const data = await res.json();
console.log("成功:", data);
} catch (e) {
// ネットワークエラーも HTTPエラーもここでまとめて処理
console.error("データ取得失敗:", e.message);
showUserMessage("サーバーとの通信に失敗しました。");
}
}
JavaScript🔎 ポイント
res.ok(HTTP ステータス 200〜299 のとき true)をチェックして、
HTTPエラーを「自分で例外化」する。- これにより、通信・HTTPどちらの失敗も一括でキャッチ可能。
❌ 悪い書き方② — async関数内で throw しても呼び出し側で無視
async function getUser() {
const res = await fetch("/api/user");
if (!res.ok) throw new Error("ユーザー取得失敗");
return res.json();
}
// ❌ 呼び出し側で try/catch を書いてない!
getUser();
console.log("続きの処理…");
JavaScript🪲 落とし穴:
getUser()内でエラーが起きても、呼び出し元がawaitしていないため、
例外は非同期のPromise内部で握りつぶされる。- コンソールには「Unhandled Promise Rejection」の警告が出るだけ。
✅ 良い書き方② — 呼び出し側も await + try/catch
async function getUser() {
const res = await fetch("/api/user");
if (!res.ok) throw new Error("ユーザー取得失敗");
return res.json();
}
async function main() {
try {
const user = await getUser();
console.log("ユーザー:", user);
} catch (e) {
console.error("getUser失敗:", e.message);
}
}
main();
JavaScript💡 ポイント
async関数が返すのは常に Promise。- その結果を
awaitしないと例外が外に出てこない。 - つまり「関数の中も外も両方で try/catch を意識」する必要がある。
❌ 悪い書き方③ — .then().catch() と async/await を混ぜる
async function fetchData() {
const res = await fetch("/api/data")
.then(res => res.json())
.catch(err => console.error("fetch失敗:", err));
console.log("結果:", res);
}
JavaScript🪲 落とし穴:
.catch()がfetch()の Promise だけに反応してしまい、
その後の JSON 処理などで起きたエラーは拾えない。awaitの中で.then()を混ぜると制御が分かりづらく、デバッグしにくい。
✅ 良い書き方③ — どちらかに統一(async/await推奨)
async function fetchData() {
try {
const res = await fetch("/api/data");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
return data;
} catch (e) {
console.error("fetchData 失敗:", e.message);
return null;
}
}
JavaScriptシンプルで追いやすく、
エラーの種類も一箇所でまとめて処理できる。
⚠️ さらによくある初級者の誤り
❌ try…catch で非同期を囲んでも効かないパターン
try {
fetch("/api/data"); // ← awaitしてない
} catch (e) {
console.error("効かない:", e); // ← 絶対にここには来ない
}
JavaScriptfetch() は非同期関数なので、例外はPromise内部で発生。
→ try…catch は同期処理にしか効かない!
✅ 対応策:必ず await または .catch() を使う
try {
const res = await fetch("/api/data");
} catch (e) {
console.error("catchできる:", e);
}
JavaScript実務的ベストプラクティス(まとめ)
| 目的 | ベストプラクティス |
|---|---|
| HTTP エラー | if (!res.ok) throw new Error(...) |
| ネットワークエラー | try…catch で捕捉 |
| 呼び出し側でも捕捉 | await を使う |
| 表示メッセージ | 開発者向け(console)とユーザー向け(UI)を分ける |
| 複数APIを順に呼ぶ | 各APIごとに try/catch を設けて影響範囲を限定する |
練習問題
次のコードを改善して、
HTTPエラーもキャッチできるようにしなさい。
async function fetchTodo() {
try {
const res = await fetch("https://example.com/api/todo");
const data = await res.json();
return data;
} catch (e) {
console.error("fetch失敗:", e);
return [];
}
}
JavaScript✅ 解答例
async function fetchTodo() {
try {
const res = await fetch("https://example.com/api/todo");
if (!res.ok) {
throw new Error(`HTTPエラー: ${res.status}`);
}
const data = await res.json();
return data;
} catch (e) {
console.error("データ取得失敗:", e.message);
return [];
}
}
JavaScript