diff --git a/source/basic/error-try-catch/README.md b/source/basic/error-try-catch/README.md index bcfbf38aff..a214770bc9 100644 --- a/source/basic/error-try-catch/README.md +++ b/source/basic/error-try-catch/README.md @@ -282,6 +282,39 @@ MDNの[JavaScriptエラーリファレンス][]には、ブラウザが投げる また、ほとんどのブラウザには`console.log`や`console.error`の出力をフィルタリングできる機能が備わっています。 ただのログ出力には`console.log`を使い、エラーに関するログ出力には`console.error`を使うことで、ログの重要度が区別しやすくなります。 +## [ES2022] Error Cause {#error-cause} + +エラーをキャッチした際、新しいメッセージを持たせた別のエラーを再度投げ直すことで、デバッグに役立つ情報を付与できます。 +これを実現する時に、新しくErrorオブジェクトを作成してthrowすることで実現できます。 +しかし、この方法には本来のエラーのスタックトレースが失われるという問題があります。 + +{{book.console}} + +```js +function somethingWork() { + throw new Error("本来のエラー"); +} + +try { + somethingWork(); +} catch (error) { + // `error` が持っていたスタックトレースが失われるため、実際にエラーが発生した場所がわからなくなる + throw new Error("somethingWork関数でエラーが発生しました"); +} +``` + +このスタックトレースが失われる問題を解決するには、ES2022で追加されたErrorの`cause`オプションが利用できます。 +新しいエラーオブジェクトを作成する際に、第2引数の`cause`オプションに本来のエラーオブジェクトを渡すことで、本来のスタックトレースを保持できます。 + +{{book.console}} +[import, error-cause/index.js](src/error-cause/index.js) + +このスクリプトを読み込むと、`sumNumsStrings` の例外に `safeParseInt` から投げられたスタックトレースが付与された状態のエラーログがコンソールに出力されます。 +ここではFirefoxにおける実行例を示します。 + + +![safeParseIntのスタックトレースが含まれたconsole.errorの出力結果](./img/error-cause.png) + ## まとめ {#conclusion} この章では、例外処理とエラーオブジェクトについて学びました。 @@ -291,6 +324,7 @@ MDNの[JavaScriptエラーリファレンス][]には、ブラウザが投げる - `throw`文は例外を投げることができ、`Error`オブジェクトを例外として投げる - `Error`オブジェクトには、ECMAScript仕様や実行環境で定義されたビルトインエラーがある - `Error`オブジェクトには、スタックトレースが記録され、デバッグに役立てられる +- Error Causeを使うことで、別のエラーのスタックトレースを引き継いだ新しいエラーを作成できる [try...catch]: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/try...catch [throw]: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/throw diff --git a/source/basic/error-try-catch/img/error-cause.png b/source/basic/error-try-catch/img/error-cause.png new file mode 100644 index 0000000000..da7a16cee0 Binary files /dev/null and b/source/basic/error-try-catch/img/error-cause.png differ diff --git a/source/basic/error-try-catch/src/error-cause/index.js b/source/basic/error-try-catch/src/error-cause/index.js new file mode 100644 index 0000000000..6a798e8294 --- /dev/null +++ b/source/basic/error-try-catch/src/error-cause/index.js @@ -0,0 +1,27 @@ +// 数値の文字列を受け取り数値を返す関数 +// 'text' など数値にはならない文字列を渡された場合は例外を投げられる +function safeParseInt(numStr) { + const num = Number.parseInt(numStr, 10); + if (Number.isNaN(num)) { + throw new Error(`${numStr} is not a numeric`); + } + return num; +} + +// 数字の文字列を二つ受け取り、合計を返す関数 +function sumNumStrings(a, b) { + try { + const aNumber = safeParseInt(a); + const bNumber = safeParseInt(b); + return aNumber + bNumber; + } catch (e) { + throw new Error("Failed to sum a and b", { cause: e }); + } +} + +try { + // 数値にならない文字列 'string' を渡しているので例外が投げられる + sumNumStrings("string", "2"); +} catch (err) { + console.error(`エラーが発生しました (${err})`); +} diff --git a/source/use-case/ajaxapp/promise/README.md b/source/use-case/ajaxapp/promise/README.md index ee71c13235..c9bbf997cf 100644 --- a/source/use-case/ajaxapp/promise/README.md +++ b/source/use-case/ajaxapp/promise/README.md @@ -100,6 +100,9 @@ function fetchUserInfo(userId) { displayView(view); }); } + }) + .catch(err => { + return Promise.reject(new Error(`Failed fetch user(id: ${userId}) info`, { cause: err })); }); } ``` @@ -151,6 +154,9 @@ function fetchUserInfo(userId) { // JSONオブジェクトで解決されるPromiseを返す return response.json(); } + }) + .catch(err => { + return Promise.reject(new Error(`Failed fetch user(id: ${userId}) info`, { cause: err })); }); } ```