はじめに
例外処理を実装するにあたって欠かせないのがtry-catch文です。これを使うことで、ランタイムエラーが発生しても処理を継続できたりするわけです。
ところでtry-catch文ではfinallyというものを使うこともできます。これはtryブロックの中が正常に完了しても、catchに進んだとしても、最後に実行される処理を書く場所です。
try {
// 何らかの処理
console.log('tryで実行');
} catch {
// エラー次の処理
console.log('catchで実行');
} finally {
// 最後に必ず行う処理
console.log('最後に必ず実行');
}
ところで、わざわざfinallyって使う必要ありますか?
try完了後(あるいはcatch完了後)に何かの処理を実行させたいなら、以下のように書けばいいはずです。
try {
// 何らかの処理
console.log('tryで実行');
} catch {
// エラー次の処理
console.log('catchで実行');
}
// 最後に必ず行う処理
console.log('最後に必ず実行');
わざわざfinallyが用意されているのはなぜでしょうか。
例外が持つ大域脱出
実は、脱出に割り込むことはfinallyにしかできません。
try-catch文は例外処理と合わせて使われることが多いですが、例外は大域脱出という特徴を持っています。大域脱出は、その場でプログラムの実行を中断して別の場所にプログラムの制御を移すことを指します。
try {
throwError();
// throwError()で例外が投げられてたので、これは実行されない
console.log('実行されない');
} catch (error) {
console.log(error);
}
function throwError () {
const error = new Error('エラーが発生しました');
throw error;
// 例外が投げられたので、これは実行されない
console.log('実行されない');
}
大域脱出には、複数箇所で発生した例外を1箇所で処理できるというメリットがあります。
finallyだけができること
この脱出にfinallyは割り込むことができます。
try {
// 何らかの処理
console.log('tryで実行');
throwError();
} finally {
// エラー次の処理
console.log('最後に実行');
}
上記のコードを実行すると、以下のような結果になります。
tryで実行
最後に実行
Uncaught Error: エラーが発生しました
at throwError (<anonymous>:2:16)
at <anonymous>:9:2
try→finallyという順に処理は進むはずなので、順番的にはUncaught Errorの後に「最後に実行」が出力されそう(正確にはその前にランタイムエラーが発生しているので出力はされない)ですが、実際にはこのような結果になります。
これは、try文から脱出する際にfinallyが実行されたことを意味しています。
このように、finallyは脱出に割り込むことができるという特性を持っています。
最後に
とはいえfinallyを使う機会はあまりないと思います。
