Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ErrorCauseへの対応 #1732

Merged
merged 19 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions source/basic/error-try-catch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,40 @@ MDNの[JavaScriptエラーリファレンス][]には、ブラウザが投げる
また、ほとんどのブラウザには`console.log`や`console.error`の出力をフィルタリングできる機能が備わっています。
ただのログ出力には`console.log`を使い、エラーに関するログ出力には`console.error`を使うことで、ログの重要度が区別しやすくなります。

## [ES2022] Error Cause {#error-cause}

エラーをキャッチした際、新しいメッセージを持たせた別のエラーを再度投げ直すことで、デバッグに役立つ情報を付与できます。

これを実現する時に、新しくErrorオブジェクトを作成してthrowすることで実現できます。

しかし、この方法には本来のエラーのスタックトレースが失われるという問題があります。
himanoa marked this conversation as resolved.
Show resolved Hide resolved

{{book.console}}
himanoa marked this conversation as resolved.
Show resolved Hide resolved
<!-- doctest:Error -->
```js
function somethingWork() {
throw new Error("本来のエラー");
}

try {
somethingWork();
} catch (error) {
// `error` が持っていたスタックトレースが失われるため、実際にエラーが発生した場所がわからなくなる
throw new Error("somethingWork関数でエラーが発生しました");
}
```

この問題を解決するには、`catch` 句で補足した本来のエラーを、新しい `Error` オブジェクトのコンストラクタに渡すことで、スタックトレースを引き継ぐことができます。
Copy link
Collaborator

@azu azu Mar 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1732 (comment)
ここで書いたように cause オプション に渡すというのを文章で説明する必要があると思います。
そうしないとcauseオプションがいきなりコードに出てきてしまってるので、驚き最小の原則の反してしまう。
(コードを読む前に、コードに書かれている想像できるような説明を書くイメージ)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほどー修正します!


{{book.console}}
[import, error-cause/index.js](src/error-cause/index.js)

このスクリプトを読み込むと、`sumNumsStrings` の例外に `safeParseInt` から投げられたスタックトレースが付与された状態のエラーログがコンソールに出力されます。
ここではFirefoxにおける実行例を示します。


![safeParseIntのスタックトレースが含まれたconsole.errorの出力結果](./img/error-cause.png)

## まとめ {#conclusion}

この章では、例外処理とエラーオブジェクトについて学びました。
Expand All @@ -291,6 +325,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
Expand Down
Binary file added source/basic/error-try-catch/img/error-cause.png
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 スクリーンショットは、後でここにまとめて撮り直す。

https://github.com/asciidwango/js-primer/blob/master/source/basic/error-try-catch/screenshot-manual.sh

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このシェルスクリプトは僕の環境で実行してコミットにスクリーンショット付ける形で大丈夫ですか?

Copy link
Contributor Author

@himanoa himanoa Mar 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これ、手元にmacOSマシンがなくて実行できなさそうでした 🙇

Copy link
Collaborator

@azu azu Mar 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

スクショは統一感が大事だと思うので(OSでUIが異なる)、一旦そのままで大丈夫ですー

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions source/basic/error-try-catch/src/error-cause/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 数字の文字列を二つ受け取り、合計を返す関数
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 });
}
}

// 数値の文字列を受け取り数値を返す関数
// 'text' など数値にはならない文字列を渡された場合は例外を投げられる
function safeParseInt(numStr) {
const num = parseInt(numStr, 10);
himanoa marked this conversation as resolved.
Show resolved Hide resolved
if (Number.isNaN(num)) {
throw new Error(`${numStr} is not a numeric`);
}
return num;
}
Copy link
Collaborator

@azu azu Mar 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

safeParseIntとsafeParseIntは順番逆にした方が良い気はします。
既知 → 未知の順番にしたいので。


try {
// 数値にならない文字列 'string' を渡しているので例外が投げられる
sumNumStrings("string", "2");
} catch (err) {
console.error(`エラーが発生しました (${err})`);
}
6 changes: 6 additions & 0 deletions source/use-case/ajaxapp/promise/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ function fetchUserInfo(userId) {
displayView(view);
});
}
})
.catch(err => {
return Promise.reject(new Error(`Failed fetch user(id: ${userId}) info`, { cause: err }));
});
}
```
Expand Down Expand Up @@ -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 }));
});
}
```
Expand Down
Loading