ここでは、「非同期処理(fetch / async / Promise)で起きたエラーをデバッグで追う」 を、
初心者にもわかるように ― 例題つきで ― 解説します。
目的
「非同期の中で発生したエラー(例外)」を stack trace(スタックトレース) から正確にたどる力をつける。
1. まず「stack trace(スタックトレース)」とは?
スタックトレースとは、
「エラーがどの関数の中で、どの行で起きたのか」
を示すエラーログの履歴です。
例:同期エラーの場合
function a() {
b();
}
function b() {
throw new Error('問題が発生しました');
}
a();
JavaScript出力例:
Uncaught Error: 問題が発生しました
at b (main.js:4)
at a (main.js:2)
at main.js:6
読み方:
- 一番上が「エラーが発生した箇所」
- 下に行くほど「その関数を呼び出した場所(呼び出しの履歴)」
2. 非同期(Promise / fetch)の場合はどうなる?
async function getData() {
const res = await fetch('https://wrong.url/api'); // ← ここで失敗
const data = await res.json();
return data;
}
async function main() {
await getData();
}
main();
JavaScriptブラウザコンソール出力:
Uncaught (in promise) TypeError: Failed to fetch
at getData (main.js:2)
at async main (main.js:7)
ポイント:
- エラー名:
TypeError: Failed to fetch(ネットワークに失敗) - 「Uncaught (in promise)」 は、「Promise の中で投げられたけど、catch されなかった」ことを意味します。
- async/await のスタックも追える(
at async main)。
❌ よくある落とし穴:スタックが途中で消える!
function fetchData() {
return fetch('https://wrong.url/api')
.then(res => res.json())
.then(data => {
throw new Error('JSON解析で失敗');
});
}
fetchData().catch(e => console.error(e));
JavaScript出力例:
Error: JSON解析で失敗
at main.js:5
😨 あれ?どこから呼ばれたか分からない…
→ Promiseチェーン(then/catch) では、
JavaScriptエンジンが非同期境界でスタックを切り離すため、
呼び出し元の履歴が失われやすい のです。
対策1:async/awaitで書く(stack traceが繋がる)
async function fetchData() {
const res = await fetch('https://wrong.url/api');
const data = await res.json();
throw new Error('JSON解析で失敗');
}
async function main() {
await fetchData();
}
main().catch(e => console.error(e));
JavaScript出力例:
Error: JSON解析で失敗
at fetchData (main.js:4)
at async main (main.js:8)
✨ 非同期の呼び出し関係(main → fetchData)がスタックトレースに残ります。
→ async/await の方がデバッグしやすい理由のひとつ!
対策2:Error にスタック情報を明示的に保持
async function doTask() {
try {
const res = await fetch('https://wrong.url/api');
return await res.json();
} catch (err) {
console.error('内部エラー:', err.stack);
throw new Error('外部呼び出し側に再スロー');
}
}
doTask().catch(e => console.error('呼び出し側:', e.stack));
JavaScript.stack プロパティをログに出すことで、
「関数階層+行番号」がはっきり確認できます。
対策3:開発ツールでブレークポイント+スタック追跡
ブラウザ(例:Chrome DevTools, Firefox DevTools)での実践:
- Sources(ソース)タブを開く
async function内の行(たとえばawait fetchの行)にブレークポイントを置く- 再読み込みまたは関数呼び出しで停止する
- 右側の Call Stack を見る
main()→getData()のような呼び出し階層が確認できる
Scopeパネルで変数の中身を確認できる
💡 非同期の「await」境界で止まるので、
ネットワークエラーやJSONパースエラーもその場で調査可能。
参考:Node.jsでのデバッグも同様
Node.jsで --inspect オプションを使えば同様に追えます。
node --inspect-brk app.js
VSCode で「Run and Debug」を開始すると、async 関数内のステップ実行や stack trace の確認が可能。
非同期例外デバッグのまとめ
| 状況 | 問題 | 解決策 |
|---|---|---|
then().catch() でしか書いていない | stack trace が途切れる | async/await を使う |
| fetch の失敗がどこで起きたかわからない | await なしの非同期呼び出し | await を使って try...catch で囲む |
| catch 内で情報が少ない | err.stack を出していない | console.error(err.stack) |
| どの関数で止まったか調べたい | 非同期関数が多層になっている | DevTools の「Call Stack」を確認 |
| 呼び出し元まで原因を知りたい | 再スロー時に情報を失う | throw new Error('説明', { cause: err })(ES2022)で原因を保持 |
発展:Error の cause プロパティ(ES2022〜)
try {
await fetch('https://wrong.url/api');
} catch (err) {
throw new Error('API取得失敗', { cause: err });
}
JavaScript後で:
try {
await doSomething();
} catch (e) {
console.error(e.message); // API取得失敗
console.error(e.cause.message); // 元のエラー(TypeError: Failed to fetch)
}
JavaScript👉 エラーの「原因」をネストして追跡できるので、複雑な非同期処理のデバッグに非常に便利です。
まとめ(初心者が押さえるべきポイント)
try...catchで非同期を捕まえるにはawaitが必要- スタックトレース(stack trace)を読む癖をつける
- 非同期処理は async/await で書く方がデバッグがしやすい
- ブラウザの DevTools の “Call Stack” で関数呼び出しをたどる
- 再スローする時は
causeプロパティで元エラーを保持


