【初心者向け】エラーを握りつぶすことの問題と対処法
今回はプログラミング初学者向けに、エラーを握りつぶすことの問題とエラーハンドリグについて解説します。
javascriptを用いて解説しますが、以下の2点は把握している前提で書いています。
- jsの基本的な構文
- try catchの使い方
エラーを握りつぶすとは
エラーを握りつぶすとは、以下のようにでエラーをキャッチしたは良いが、エラーが発生したときの処理を何も書かかないことで、エラーが発生したことを分からなくてしまうことを言います。
try {
throw new Error('エラーメッセージ')
} catch (error) {
}
例えば、以下のコードを実行すると
const throwError = () => {
try {
throw new Error('エラーメッセージ')
} catch (error) {
}
}
const func = () => {
console.log('start')
throwError()
console.log('finish')
}
func()
出力結果はstartとfinishが出力されるだけになります。
start
finish
実際にはstartとfinishの出力の間でエラーが発生していますが、throwError関数でエラーを握りつぶしているためエラーが発生していることに気づくことができません。
エラーを握りつぶすことの問題は、主に2点あります。
- デバッグが困難になる
- ユーザー体験が悪い
デバッグが困難になる
具体例で見たように、エラーを握りつぶすと実際にはエラーが発生しているが気付けない状態となるため、システムがなぜ動作しないのか、何が原因で動作していないのかを特定することが難しくなります。
ユーザー体験が悪い
開発者がエラーの原因を把握できないということは、当然ユーザーもエラーの原因を把握することができません。
せめてエラーが発生していることが分かるようなアラートを出せれば良いですが、エラーを握りつぶしている場合、エラーが発生しているかどうかもユーザーに伝えることができなくなります。
対処方法
ログを出力する
開発者がデバッグをしやすいように、エラーのログを出力するようにしましょう。
const throwError = () => {
try {
throw new Error('エラーメッセージ')
} catch (error) {
// エラーのログを出力する
console.log(error)
}
}
const func = () => {
console.log('start')
throwError()
console.log('finish')
}
func()
出力結果
start
Error: エラーメッセージ
finish
ログを出力するようにしたことで、エラーが発生していることに気付けるようになりました。
エラーが発生した箇所で処理を止めたい場合は、throwError関数のcatch内でエラーを投げるようにしましょう。
const throwError = () => {
try {
throw new Error('エラーメッセージ')
} catch (error) {
throw new Error(error.message)
}
}
const func = () => {
console.log('start')
throwError()
console.log('finish')
}
func()
出力結果
start
Error: エラーメッセージ
ユーザーに通知する
開発者に分かりやすいように、ログを出力するとともに、必要に応じてユーザーにもエラーを確認できるようにしましょう。
const throwError = () => {
try {
throw new Error('エラーメッセージ')
} catch (error) {
throw new Error(error.message)
}
}
const func = () => {
try {
console.log('start')
throwError()
console.log('finish')
} catch (error) {
// アラートを出す
alert('エラーが発生しました。')
}
}
func()
これにより、ユーザーもエラーが発生していることが分かるようになり、操作ができないなどの問題があってもエラーが起きていることが原因だと分かります。
今回は簡易的にアラートを出していますが、実際のアプリケーションではエラーの詳細や解決策を示すとよりユーザー体験の向上に繋がります。
気をつけるべきポイント
エラーハンドリングは関数のネストが深くなったり、システムが大規模になってくるとより複雑になっていきます。
例えば、以下のコードを実行すると
const throwError = () => {
try {
throw new Error('エラーメッセージ')
} catch (error) {
throw new Error(error.message)
}
}
const func = () => {
try {
console.log('func start')
throwError()
console.log('func finish')
} catch (error) {
console.log(error.message)
}
}
const main = () => {
console.log('main start')
func()
console.log('main finish')
}
main()
出力結果は以下のようになります。
main start
func start
エラーメッセージ
main finish
ここでのポイントは「main finish」が出力されていることです。
main関数ではエラーハンドリグを行っていないため、このような挙動になります。
これが期待通りの場合は問題ありませんが、throwErrorのエラーが発生した場合、main関数の処理も止めた場合には問題です。
main関数の処理も止めたい場合、どのように対処するべきでしょうか。
対処方法はいくつかありますが、例えば以下の2点があります。
- func関数のcatch内でも、エラーを投げるようにする
- func関数のtry catchをやめる
これらの共通点は、main関数にエラーを伝えていることです。
このように、システムが複雑になってきて階層が深くなると、エラーハンドリグをすべき場所、しなくても良い場所など、エラーを握りつぶさないために、正しくエラーを伝搬する必要がでてきます。
とりあえずtry catchを使用してログを出力すれば良い訳ではないので、エラーを適切に対処できているかは常に気をつけましょう。