エラーを無視せず、どこに問題があるのか、きちんと確認できるコードの書き方をデモで紹介。Krögerが査読を担当しています。最良の記事を提供することができ、SitePointの査読担当者の皆さんに感謝します。この記事では、JavaScriptのエラー処理について考え、その落とし穴から便利な実践例までを説明します。最後には、非同期コードとAjaxにも触れます。JavaScriptはイベント駆動型プログラムで、プログラミングをより豊かなものにしてくれます。ブラウザーをイベント駆動型プログラムと考えると、発生するエラーは同一のものとなります。エラーが発生した際、あるポイントでイベントが発生します。理論上、JavaScriptでのエラーは単純なイベントともいえます。そんなことは聞いたことがないと思っても、騙されたと思って続きを読んでみてください。この記事では、クライアントサイドのJavaScriptにのみ焦点をあてています。JavaScriptという記事の中で説明されたコンセプトに基づいて書かれています。「エラーが発生した場合、JavaScriptはコールスタックに対処しならがら、エラーを検索する」ということです。このことに得心がいかなければ、基本から勉強することをお勧めします。ここでは、エラー対処の応用編を説明します。デモこの記事で使用するデモはGitHubで入手可能です。デモを実行すると次のようなページが表示されます。どのボタンをクリックしても「ボム」が起動します。このボムはTypeErrorとして作られたエラーをシミュレーションします。次のコードはこのようなユニットテストのモジュール定義です。foo.bar();}この関数はfooと呼ばれる空のオブジェクトを指定します。bar()の定義がどこにもないことに注意してください。この関数がユニットテストでボムを起動できるか確かめてみましょう。TypeError);});このユニットテストはShould.jsでアサーションをMochaに記録しています。Should.jsがアサーションライブラリなのに対し、Mochaはテストランナーです。分からなければ、APIsテストを参照してください。テストはit('description')から始まり、shouldのパスまたは失敗で終わります。ここで朗報ですが、ユニットテストはノード上で動作し、ブラウザーを必要としません。ユニットテストに注目するのは、JavaScriptでのキーコンセプトだからです。上のように、error()は空のオブジェクトを定義してから、メソッドにアクセスしようとします。bar()はerror()が引き起こすエラーのオブジェクトに存在しないからです。心配しないでください。JavaScriptのような動的なプログラミング言語では、そういったことは普通に起こる場合があるのです。悪いエラーハンドリング悪いエラーハンドリングについて説明しましょう。実装されたデモからエラーハンドリングを取り出します。以下のコードで、ユニットテストの処理を見てみましょう。null;}この処理は、依存関係としてfnコールバックを受け取ります。この依存関係は処理関数の内部に呼び出され、ユニットテストは、どのように使われるのか示します。should(result).equal(null);});見ての通り、この処理は何か問題が生じた場合、nullを返し、fn()コールバックは正式なメソッドやボムを示します。以下のクリック処理が続きです。error));ここで問題なのはnullを取得すると、エラーの特定ができなくなることです。このようなフェイルサイレント戦略は、悪いUXからデータの破壊にまでおよんでしまいます。厄介なのは何時間もかけて症状をデバッグしても、try-catchブロックを認識できないことです。このような処理はコード内のエラーを取り込んで、あたかもすべて問題がないかのように見せてしまうのです。コード品質を追求しない組織には向いているかもしれませんが、エラーが特定できないせいで何時間もデバックが必要になるでしょう。コールスタックが深くなる多層なソリューションでは、エラーの発生した箇所を特定するのは不可能でしょう。まれにtry-catchが有効な場合があるかもしれませんが、エラー処理に関する限りは悪いことでしかありません。フェイルサイレント戦略は、より良いエラーハンドリングの妨げとなってしまいます。JavaScriptはこのような問題に対処する、さらに有効な方法を提供しています。醜いエラーハンドリング続いて、醜いエラーハンドリングについてです。ここでは、DOMに深く関わる事柄については触れないことにします。いうのも、先の悪いエラーハンドリングと何ら違いはないからです。重要なのは、以下のように、ユニットテストにおいてひどい処理がエラーをどのように収拾するかということです。Error);});悪いエラーハンドリングからは明らかな改善が見られます。エラーがコールスタックを呼び出すからです。良いところは、エラーはデバッグに非常に便利なスタックをアンワインドすることです。エラーが発生しても、インタープリターは別の処理を検索してスタックを移動します。して、コールスタックのトップでエラーに対処することが多くなります。私は、不幸にもこのひどい処理で、最初のエラーを見つけられませんでした。したがって、最初のエラーを特定するスタックをトラバースし直さなくてはいけませんでしたが、少なくともエラーが起きたことは認識できました。醜いエラーハンドリングは有害ではありませんが、コードスメルを引き起こします。ブラウザーがこれに対処する機能を搭載しているか確認してみてください。スタックのアンワインドエラーをアンワインドする方法の1つに、コールスタックのトップにtry...catchを入れるというものがあります。}}ここで考えて欲しいのは、ブラウザーはイベント駆動型プログラムなのかということです。JavaScriptにおけるエラーはイベントに他なりません。インタープリターは現在実行中のコンテキストとアンワインドを停止します。結果として、ユーザーが活用可能なonerrorグローバルイベントハンドラーが、次のようになります。console.log(error);});このイベントハンドラーは実行中のコンテキストでエラーを認識します。エラーイベントは、何らかの種類のエラーとみなされ、さまざまなターゲットから実行されます。非常に画期的なこととして、そのイベントハンドラーはコード内でエラーハンドリングを集中します。他のイベントと同じく、特定のエラーを認識するために、デイジーチェーンハンドラーになります。これにより、SOLIDの原理に従う場合、エラーハンドラーは単一の目的になります。このようなハンドラーはいつでも登録できます。従って、インタープリターはできるだけ多くのハンドラーを循環し、コードベースはいたるところにあるtry...catchブロックから解放され、デバッグが容易になります。重要なのは、JavaScriptにおいてはエラーハンドリングをイベントハンドリングと同じように扱うことでしょう。コールスタックが常に利用可能になるということかもしれません。スタックのキャプチャーコールスタックはトラブルシューティングに非常に便利です。ブラウザーがトラブルを表示します。エラーオブジェクトのstackプロパティーは標準ではありませんが、最新のブラウザーでは使えます。このプロパティーの便利なことは、エラーをサーバーに記憶するということです。xhr.send(message);});コードでは明らかではありませんが、このイベント処理はプロパティーとともに実行します。どの処理も、DRYコードを持つ単一の目的になります。ここで便利なのは、このようなメッセージがサーバー上で取得される仕組みです。ノードにおいて、メッセージはこのように表示されます。適切なエラー処理では、何が問題であるかが驚くほど簡単に認識できます。ミスを隠す必要はありません。一目見れば、何が、どこでエラーを引き起こしたのかが分かるからです。このような透明性はフロントエンドコードのデバッグに非常に役に立ちます。メッセージは、あとあと復旧できるように永続的にストレージに蓄積され、どのような状況がどのようなエラーを引き起こすか理解を深められます。JavaScriptは実行中のコンテキストから非同期コードを取り込みます。以下に示されるようなtry...catchステートメントには問題があるということです。}}続きはこのユニットテストを見れば分かります。});}エラーを特定するために、プロミス処理でラップしなければなりませんでした。良いとされるtry...catch周辺にコードブロックがありますが、対処されていないエラーが起きていることに注意しなければいけません。try...catchステートメントは単一の実行コンテキスト内でのみ動作します。エラーが発生するまでに、インタープリターはtry...catchから移動しました。同じ現象はAjaxコールでも発生します。そのため、2つの選択肢があります。その1つは非同期コールバック内で例外を特定することです。1);このアプローチは有効かもしれませんが、もう少し改善できます。try...catchブロックはあちらこちらで、もつれています。実際に1970年代のプログラコードを戻そうとしています。functionsの使用を妨げます(V8はChromeやNodeで使用されるJavaScriptのエンジン)。2つ目の選択肢として、コールスタックのトップにそのようなブロックの入力が推奨されます。このことから、ユーザーにどのようなメリットが生まれるのでしょうか。実行中のコンテキスト内で、グローバルエラー処理が動作すると言った理由がこれです。グローバルエラー処理は便利ですっきりとしたコードを書く手助けになります。次の図は、この例外処理がサーバー上で何を記録するのかの表示です。デモコードを使う場合、表示画面は使用するブラウザーによって多少異なるので注意してください。この処理によりエラーは非同期コードとsetTimeout()で発生していることが分かりました。素晴らしいですね!まとめエラー処理の分野では、少なくとも2つのアプローチがあります。1つはコード内でエラーを無視するフェイルサイレント手法です。1つはエラーが実行しているコンテクストを停止して元に戻す、フェイルファストアンワインド手法です。どちらが便利な手法でなぜそうなのかは明らかだと思います。私ならもちろん、エラーを無視しない方法を選択します。プログラム内で生じるアクシデントを理由に責める人などいません。停止し、元に戻し、ユーザーに別の選択肢を示すのも良いでしょう。完璧などない世の中なので、重要なのはセカンドチャンスがあることではないでしょうか。エラーは必ず起こります。大切なのはそれをどう対処するかということです。Reyesテキサス州ヒューストン在住の、夫であり、父であり、そしてソフトウェア技術者でもあります。JavaScript、C#、webbingに情熱を注いでいます。