finallyは単なる「最後に行う処理」ではない
フロントエンド開発において、try-catchを用いたエラーハンドリングは基本中の基本です。しかし、実務で多くのコードレビューを行う中で、finallyブロックの重要性が過小評価されていると感じることが多々あります。多くの開発者は「エラーがあってもなくても実行したい処理」という程度の認識ですが、実はfinallyは「リソースの解放」と「アプリケーションの整合性維持」において、最も信頼できる防御策なのです。
なぜ「catchの後」では不十分なのか
よくあるアンチパターンとして、catchブロックの最後に共通処理を書くケースがあります。例えば、APIリクエスト中のローディングインジケーターを非表示にする処理を、tryとcatchの両方に記述してしまうパターンです。これはDRY原則に反するだけでなく、将来的にその関数が拡張され、エラー処理が分岐した際に「片方の経路で非表示処理が漏れる」というバグを誘発します。
finallyブロックは、tryのスコープ内であれば、return文が実行された後であっても必ず呼び出されます。 つまり、関数のどこで早期リターンしようとも、確実に処理を完遂できる唯一の場所なのです。
実務で差がつく「クリーンアップ」の事例
現場で私が特に意識しているのは、メモリリークや状態の不整合を防ぐためのfinally活用です。例えば、以下のケースを想像してください。
事例:モーダルの排他制御
特定の非同期処理中、画面上にグローバルなオーバーレイ(読み込み中画面)を表示し、ユーザーの入力をブロックする機能を作るとします。
コード例:
try {
setLoading(true);
await fetchData();
} catch (error) {
handleError(error);
} finally {
setLoading(false);
}
このfinallyがないと、もしfetchDataで予期せぬ例外が発生し、かつcatchブロック内で別の例外が発生した場合、ローディング状態が永遠に解除されず、UIがフリーズしたままになります。finallyは、アプリケーションの「現在の状態」を正常な状態へ戻すためのセーフティネットです。
複雑な非同期処理における「状態の正規化」
最近のReact開発では、複雑なUI状態を管理することが多いですが、finallyブロック内で状態をリセットする癖をつけておくと、デバッグの難易度が劇的に下がります。
特に、タイマーのクリア(clearTimeout)、外部ライブラリのインスタンス破棄、イベントリスナーの解除などをfinallyに集約させることで、「成功した時だけクリーンアップする」という脆弱な設計から脱却できます。
結論:finallyは「防御的プログラミング」の要
コードを書く際、「ここで例外が起きたら、次に何が残留してしまうか?」を常に自問してください。その答えが、finallyブロックに書くべき処理です。
単にコードを簡潔にするためではなく、どんな状況下でもアプリケーションを「クリーンな状態」に保つことこそが、プロフェッショナルなフロントエンドエンジニアが追求すべき品質です。今日から、エラーハンドリングの設計を見直し、finallyを「必須の防波堤」として活用してみてください。