この README には、JavaScript エラーへの対処、サーバーへのエラー報告、これをすべて困難にする多くのバグへの対処方法について、私が長年にわたって学んだ情報が含まれています。この分野ではブラウザは改善されていますが、すべてのアプリケーションが発生したエラーを正常かつ確実に処理できるようにするためには、まだ改善の余地が残っています。
このガイドに記載されている内容のテスト ケースは、https://mknichel.github.io/javascript-errors/ にあります。
目次
導入
JavaScript エラーの構造
JavaScript エラーの生成
エラーメッセージ
スタックトレースのフォーマット
JavaScript エラーの捕捉
window.onerror
トライ/キャッチ
保護されたエントリポイント
約束
ウェブワーカー
Chrome 拡張機能
エラーを捕捉、報告し、修正することは、アプリケーションの健全性と安定性を確保するために重要な部分です。 JavaScript コードはクライアントやさまざまなブラウザ環境でも実行されるため、アプリケーションからの JS エラーを常に把握することも困難になる場合があります。各ブラウザの実装に違いが生じる JS エラーを報告する方法に関する正式な Web 仕様はありません。さらに、ブラウザの JavaScript エラーの実装にも多くのバグがあり、これがさらに困難になっています。このページでは、JS エラーのこれらの側面をナビゲートして、将来の開発者がエラーをより適切に処理できるようにし、ブラウザーが標準化されたソリューションに収束することを期待しています。
JavaScript エラーは、エラー メッセージとスタック トレースという 2 つの主要な部分で構成されます。エラー メッセージは何が問題だったかを説明する文字列であり、スタック トレースはコード内のどこでエラーが発生したかを説明します。 JS エラーは、ブラウザ自体によって生成されることも、アプリケーション コードによってスローされることもあります。
JS エラーは、コードの一部が適切に実行されない場合にブラウザによってスローされることも、コードによって直接スローされることもあります。
例えば:
var a = 3;a();
この例では、実際には数値である変数を関数として呼び出すことはできません。ブラウザーは、 TypeError: a is not a function
のようなエラーをスローします。
開発者は、特定の前提条件が満たされない場合にコードの一部でエラーをスローしたい場合もあります。例えば
if (!checkPrecondition()) { throw new Error("前提条件を満たしていません!");}
この場合、エラーはError: Doesn't meet precondition!
になります。 。このエラーには、適切な行を指すスタック トレースも含まれます。ブラウザーとアプリケーション コードによってスローされたエラーは同じように処理できます。
開発者が JavaScript でエラーをスローする方法は複数あります。
throw new Error('Problem description.')
throw Error('Problem description.')
<-- 最初のものと同等
throw 'Problem description.'
<-- 悪い
throw null
<-- さらに悪いことに
ブラウザーはそのエラーにスタック トレースを添付しないため、コード内でエラーが発生した場所のコンテキストが失われるため、文字列または null をスローすることは実際には推奨されません。実際の Error オブジェクトをスローするのが最善です。このオブジェクトには、エラー メッセージと、エラーが発生したコードの正しい行を示すスタック トレースが含まれます。
各ブラウザには、関数以外を呼び出そうとする上記の例など、組み込み例外に使用する独自のメッセージ セットがあります。ブラウザは同じメッセージを使用しようとしますが、仕様がないため、これは保証されません。たとえば、Chrome と Firefox は両方とも、上記の例では{0} is not a function
が、IE11 はFunction expected
を報告します (特に、どの変数が呼び出されようとしたかは報告されません)。
ただし、ブラウザーも頻繁に異なる傾向があります。 switch
ステートメントに複数のデフォルト ステートメントがある場合、Chrome は"More than one default clause in switch statement"
をスローしますが、Firefox は"more than one switch default"
を報告します。新しい機能が Web に追加されると、これらのエラー メッセージを更新する必要があります。これらの違いは、後で難読化されたコードから報告されたエラーを処理しようとするときに影響する可能性があります。
ブラウザがエラー メッセージに使用するテンプレートは、次の場所にあります。
Firefox - http://mxr.mozilla.org/mozilla1.9.1/source/js/src/js.msg
Chrome - https://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/messages.js
Internet Explorer - https://github.com/Microsoft/ChakraCore/blob/4e4d4f00f11b2ded23d1885e85fc26fcc96555da/lib/Parser/rterrors.h
ブラウザーは、一部の例外に対して異なるエラー メッセージを生成します。
スタック トレースは、コード内のどこでエラーが発生したかを説明します。これは一連のフレームで構成されており、各フレームはコード内の特定の行を記述します。最上位のフレームはエラーがスローされた場所であり、後続のフレームは関数呼び出しスタック、つまりエラーがスローされたポイントに到達するまでにコードがどのように実行されたかです。 JavaScript は通常連結および縮小されるため、特定の行に多数のステートメントがある場合に正確なステートメントを見つけられるように列番号を付けることも重要です。
Chrome の基本的なスタック トレースは次のようになります。
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9) at http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
各スタック フレームは、関数名 (該当する場合、およびコードがグローバル スコープで実行されなかった場合)、関数の元のスクリプト、およびコードの行番号と列番号で構成されます。
残念ながら、スタック トレース形式には標準がないため、ブラウザによって異なります。
Microsoft Edge と IE 11 のスタック トレースは、グローバル コードが明示的にリストされている点を除けば Chrome と似ています。
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:3) at Global code (http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3)
Firefox のスタック トレースは次のようになります。
throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9 @http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
Safari の形式は Firefox の形式に似ていますが、少し異なります。
throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:18 global code@http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:13
基本的な情報は同じですが、形式が異なります。
また、Safari の例では、形式が Chrome と異なるだけでなく、列番号も Chrome と Firefox の両方と異なることに注意してください。列番号は、コード(function namedFunction() { throwError(); })();
のように、さまざまなエラー状況でさらにずれる可能性もあります。 , Chrome はthrowError()
関数呼び出しの列を報告しますが、IE11 は文字列の先頭として列番号を報告します。これらの違いは、後でサーバーが報告されたエラーのスタック トレースを解析し、難読化されたスタック トレースを難読化する必要があるときに再び影響を及ぼします。
エラーのスタック プロパティの詳細については、https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack を参照してください。 Error.stack プロパティにアクセスすると、Chrome ではスタックの一部としてエラー メッセージが含まれますが、Safari 10 以降では含まれません。
スタック トレースの形式はブラウザによって使用される形式と列番号が異なります。
さらに詳しく見てみると、スタック トレース形式には多くの微妙な違いがあります。これについては、以下のセクションで説明します。
デフォルトでは、匿名関数には名前がなく、スタック トレースの関数名に空の文字列または「匿名関数」として表示されます (ブラウザによって異なります)。デバッグを改善するには、すべての関数に名前を追加して、スタック フレームに確実に表示されるようにする必要があります。これを行う最も簡単な方法は、たとえその名前が他の場所で使用されていない場合でも、匿名関数に名前を付けて指定することです。例えば:
setTimeout(匿名関数の関数名() { ... }, 0);
これにより、スタック トレースが次のようになります。
at http://mknichel.github.io/javascript-errors/javascript-errors.js:125:17
に
at nameOfTheAnonymousFunction (http://mknichel.github.io/javascript-errors/javascript-errors.js:121:31)
Safari では、これは次のようになります。
https://mknichel.github.io/javascript-errors/javascript-errors.js:175:27
に
nameOfTheAnonymousFunction@https://mknichel.github.io/javascript-errors/javascript-errors.js:171:41
このメソッドにより、関数内のコードのフレームにnameOfTheAnonymousFunction
が確実に表示されるため、デバッグがはるかに簡単になります。詳細については、http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/#toc-debugging-tips を参照してください。
関数自体に名前がない場合、ブラウザーは関数が割り当てられている変数またはプロパティの名前も使用します。たとえば、
var fnVariableName = function() { ... };
ブラウザは、スタック トレース内の関数の名前としてfnVariableName
使用します。
at throwError (http://mknichel.github.io/javascript-errors/javascript-errors.js:27:9) at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
それよりもさらに微妙なのは、この変数が別の関数内で定義されている場合、Firefox を除くすべてのブラウザは、スタック トレース内の関数名として変数の名前だけを使用します。Firefox は、次の名前を連結した別の形式を使用します。外部関数と内部変数の名前。例:
関数 throwErrorFromInnerFunctionAssignedToVariable() { var fnVariableName = function() { throw new Error("foo"); }; fn変数名();}
Firefox では次のように生成されます。
throwErrorFromInnerFunctionAssignedToVariable/fnVariableName@http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37
他のブラウザでは、これは次のようになります。
at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
Firefox は、別の関数内で定義された関数に対して異なるスタック フレーム テキストを使用します。
関数の表示名は、IE11 を除くすべての主要なブラウザーで、 displayName
プロパティによって設定することもできます。これらのブラウザでは、displayName は devtools デバッガに表示されますが、Safari を除くすべてのブラウザでは、エラー スタック トレースでは使用されません(Safari は、エラーに関連付けられたスタック トレースでも displayName を使用する点で他のブラウザとは異なります)。
var someFunction = function() {};someFunction.displayName = " # 関数の詳しい説明。";
displayName プロパティの公式仕様はありませんが、すべての主要なブラウザでサポートされています。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName および http://www.alertdebugging.com/2009/04/29/building-a-better を参照してください。 displayName の詳細については、-javascript-profiler-with-webkit/ を参照してください。
IE11 は、displayName プロパティをサポートしていません。
Safari は、エラー スタック トレースのシンボル名として displayName プロパティを使用します。
スタック トレースなしでエラーが報告された場合 (これが発生する場合の詳細については、以下を参照してください)、プログラムでスタック トレースをキャプチャすることが可能です。
Chrome では、 Error.captureStackTrace
API を使用すると、これを非常に簡単に行うことができます。この API の使用方法の詳細については、https://github.com/v8/v8/wiki/Stack%20Trace%20API を参照してください。
例えば:
関数ignoreThisFunctionInStackTrace() { var err = 新しいエラー(); Error.captureStackTrace(err、スタックトレースのこの関数を無視); err.stack を返します;}
他のブラウザでは、新しいエラーを作成し、そのオブジェクトのスタック プロパティにアクセスすることによってスタック トレースを収集することもできます。
var err = new Error('');return err.stack;
ただし、IE10 は、実際にエラーがスローされたときにのみスタック トレースを設定します。
試す { throw new Error('');} catch (e) { e.stack を返します;}
これらのアプローチがいずれも機能しない場合は、 arguments.callee.caller
オブジェクトを反復処理することで、行番号や列のない大まかなスタック トレースを作成できます。ただし、これは ES5 Strict モードでは機能せず、推奨されるアプローチではありません。
コードでsetTimeout
使用する場合や Promises を使用する場合など、非同期ポイントが JavaScript コードに挿入されるのは非常に一般的です。これらの非同期エントリ ポイントにより、新しい実行コンテキストが形成され、スタック トレースが再び最初から開始されるため、スタック トレースに問題が発生する可能性があります。
Chrome DevTools は非同期スタック トレースをサポートしています。つまり、エラーのスタック トレースには、非同期ポイントが導入される前に発生したフレームも表示されるようになります。 setTimeout を使用すると、最終的にエラーを生成した setTimeout 関数を呼び出したユーザーが捕捉されます。詳細については、http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/ を参照してください。
非同期スタック トレースは次のようになります。
throwError @ throw-error.js:2 setTimeout (async) throwErrorAsync @ throw-error.js:10 (anonymous function) @ throw-error-basic.html:14
非同期スタック トレースは、現時点では Chrome DevTools でのみサポートされており、DevTools が開いているときにスローされる例外に対してのみサポートされています。コード内の Error オブジェクトからアクセスされるスタック トレースには、その一部として非同期スタック トレースが含まれません。
場合によっては、非同期スタック トレースをポリフィルすることは可能ですが、スタック トレースのキャプチャは安価ではないため、アプリケーションのパフォーマンスに重大な影響を与える可能性があります。
Chrome DevTools のみが非同期スタック トレースをネイティブにサポートしています。
HTML ページに評価またはインライン化されたコードのスタック トレースでは、ページの URL と実行されたコードの行/列番号が使用されます。
例えば:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9) at http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
これらのスクリプトが実際に最適化の理由でインライン化されたスクリプトからのものである場合、URL、行、列番号は間違っています。この問題を回避するために、Chrome と Firefox は//# sourceURL=
注釈をサポートしています (Safari、Edge、および IE はサポートしません)。このアノテーションで指定された URL はすべてのスタック トレースの URL として使用され、行番号と列番号は HTML ドキュメントではなくタグの開始を基準にして計算されます。上記と同じエラーの場合、sourceURL アノテーションを値「inline.js」で使用すると、次のようなスタック トレースが生成されます。
at throwError (http://mknichel.github.io/javascript-errors/inline.js:8:9) at http://mknichel.github.io/javascript-errors/inline.js:12:3
これは、インライン スクリプトと eval を使用している場合でもスタック トレースが正しいことを確認するための非常に便利なテクニックです。
http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl では、sourceURL アノテーションについて詳しく説明しています。
Safari、Edge、および IE は、インライン スクリプトおよび eval に名前を付けるための sourceURL アノテーションをサポートしていません。 IE または Safari でインライン スクリプトを使用し、コードを難読化すると、それらのスクリプトから発生するエラーの難読化を解除できなくなります。
Chrome 42 まで、Chrome は、sourceURL 注釈を使用するインライン スクリプトの行番号を正しく計算していませんでした。詳細については、https://bugs.chromium.org/p/v8/issues/detail?id=3920 を参照してください。
インライン スクリプトのスタック フレームの行番号は、sourceURL アノテーションが使用されている場合、インライン スクリプト タグの先頭ではなく HTML ドキュメントの先頭を基準にしているため、正しくありません (正しい難読化解除が不可能になります)。 https://code.google.com/p/chromium/issues/detail?id=578269
eval を使用するコードの場合、sourceURL アノテーションを使用するかどうか以外にもスタック トレースに違いがあります。 Chrome では、eval で使用されるステートメントのスタック トレースは次のようになります。
Error: Error from eval at evaledFunction (eval at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3),:1:36) at eval (eval at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3), :1:68) at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3)
MS Edge と IE11 では、これは次のようになります。
Error from eval at evaledFunction (eval code:1:30) at eval code (eval code:1:2) at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3)
サファリの場合:
Error from eval evaledFunction eval code eval@[native code] evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:7
そしてFirefoxでは:
Error from eval evaledFunction@http://mknichel.github.io/javascript-errors/javascript-errors.js line 137 > eval:1:36 @http://mknichel.github.io/javascript-errors/javascript-errors.js line 137 > eval:1:11 evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3
これらの違いにより、すべてのブラウザで同じ評価コードを解析することが困難になる可能性があります。
各ブラウザは、eval 内で発生したエラーに対して異なるスタック トレース形式を使用します。
JavaScript コードはネイティブ コードから直接呼び出すこともできます。 Array.prototype.forEach
は良い例ですforEach
に関数を渡すと、JS エンジンがその関数を呼び出します。
関数 throwErrorWithNativeFrame() { var arr = [0, 1, 2, 3]; arr.forEach(関数という名前のFn(値) {throwError(); });}
これにより、ブラウザごとに異なるスタック トレースが生成されます。 Chrome と Safari は、次のように、スタック トレース自体にネイティブ関数の名前を別のフレームとして追加します。
(Chrome) at namedFn (http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5) at Array.forEach (native) at throwErrorWithNativeFrame (http://mknichel.github.io/javascript-errors/javascript-errors.js:152:7) (Safari) namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:15 forEach@[native code] throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:14 (Edge) at namedFn (http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5) at Array.prototype.forEach (native code) at throwErrorWithNativeFrame (http://mknichel.github.io/javascript-errors/javascript-errors.js:152:7)
ただし、Firefox と IE11 では、 forEach
がスタックの一部として呼び出されたことが表示されません。
namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5 throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:3
ブラウザによっては、スタック トレースにネイティブ コード フレームが含まれる場合と、含まれない場合があります。
アプリケーションにエラーがあったことを検出するには、一部のコードがそのエラーを捕捉し、それについて報告できる必要があります。エラーを検出するための手法は複数ありますが、それぞれに長所と短所があります。
window.onerror
エラーの検出を始めるための最も簡単で最良の方法の 1 つです。 window.onerror
関数に割り当てると、アプリケーションの別の部分で捕捉されなかったエラーが、エラーに関する情報とともにこの関数に報告されます。例えば:
window.onerror = function(msg, url, line,col, err) { console.log('アプリケーションでエラーが発生しました: ' + msg); console.log('スタック トレース: ' + err.stack);}
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror でこれについて詳しく説明しています。
歴史的に、このアプローチにはいくつかの問題がありました。
Error オブジェクトが提供されていません
window.onerror
関数の 5 番目の引数は、Error オブジェクトであると想定されています。これは 2013 年に WHATWG 仕様に追加されました: https://html.spec.whatwg.org/multipage/webappapis.html#errorevent。 Chrome、Firefox、および IE11 は、Error オブジェクト (重要なスタック プロパティとともに) を適切に提供するようになりましたが、Safari、MS Edge、および IE10 は提供しません。これは、Firefox 14 (https://bugzilla.mozilla.org/show_bug.cgi?id=355430) 以降の Firefox と、2013 年後半以降の Chrome (https://mikewest.org/2013/08/debugging-runtime-errors) で動作します。 -with-window-onerror、https://code.google.com/p/chromium/issues/detail?id=147127)。 Safari 10 では、window.onerror の Error オブジェクトのサポートが開始されました。
Safari (10 未満のバージョン)、MS Edge、および IE10 は、window.onerror 内のスタック トレースを含む Error オブジェクトをサポートしていません。
クロスドメインサニタイズ
Chrome では、window.onerror ハンドラー内の別のドメインから発生したエラーは、「Script error.」、「」、0 にサニタイズされます。エラーが別のドメインから発生した場合に、そのエラーを処理したくない場合は、これで通常は問題ありません。これにより、アプリケーションは次のようなエラーを除外できます。ただし、これは Firefox、Safari、IE11 では発生しません。また、Chrome では、問題のあるコードをラップする try/catch ブロックに対してもこれが発生しません。
Chrome のwindow.onerror
でクロスドメイン スクリプトからのエラーを完全に忠実に受信したい場合は、それらのリソースが適切なクロスオリジン ヘッダーを提供する必要があります。詳細については、https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror を参照してください。
Chrome は、別の原因から発生したエラーをサニタイズできる唯一のブラウザです。これらを慎重にフィルタリングするか、適切なヘッダーを設定してください。
Chrome 拡張機能
古いバージョンの Chrome では、ユーザーのマシンにインストールされている Chrome 拡張機能によってエラーがスローされ、window.onerror に報告される場合もありました。この問題は、Chrome の新しいバージョンでは修正されています。以下の専用の Chrome 拡張機能セクションを参照してください。
window.addEventListener("error")
API は window.onerror API と同じように機能します。このアプローチの詳細については、http://www.w3.org/html/wg/drafts/html/master/webappapis.html#runtime-script-errors を参照してください。
window.onerror 経由でエラーをキャッチしても、そのエラーが DevTools コンソールに表示されるのを防ぐことはできません。開発者はエラーを簡単に確認できるため、これは開発にとって適切な動作であると考えられます。これらのエラーが本番環境でエンド ユーザーに表示されることを望まない場合は、window.addEventListener アプローチを使用してe.preventDefault()
を呼び出すことができます。
window.onerror は、JS エラーを捕捉して報告するための最良のツールです。有効な Error オブジェクトとスタック トレースを持つ JS エラーのみをサーバーに報告することをお勧めします。そうしないと、エラーの調査が困難になったり、Chrome 拡張機能やクロスドメイン スクリプトから大量のスパムを受信したりする可能性があります。
上記のセクションを考慮すると、残念ながら、すべてのブラウザでwindow.onerror
に依存してすべてのエラー情報を取得することはできません。例外をローカルでキャッチするには、try/catch ブロックを選択するのが明白です。また、JavaScript ファイル全体を try/catch ブロックでラップして、window.onerror では捕捉できないエラー情報を捕捉することもできます。これにより、window.onerror をサポートしていないブラウザの状況は改善されますが、いくつかの欠点もあります。
try/catch ブロックは、 window.setTimeout
を通じてコードの非同期ブロックからスローされるエラーなど、プログラム内のすべてのエラーをキャプチャするわけではありません。 Try/catch を保護されたエントリ ポイントと併用すると、ギャップを埋めることができます。
アプリケーション全体をラップする try/catch ブロックだけでは、すべてのエラーをキャッチするのに十分ではありません。
古いバージョンの V8 (および他の JS エンジンの可能性もあります) では、try/catch ブロックを含む関数はコンパイラーによって最適化されません (http://www.html5rocks.com/en/tutorials/speed/v8/)。 Chrome は TurboFan でこの問題を修正しました (https://codereview.chromium.org/1996373002)。
JavaScript への「エントリ ポイント」は、コードの実行を開始できるブラウザ API です。例には、 setTimeout
、 setInterval
、イベント リスナー、XHR、Web ソケット、または Promise が含まれます。これらのエントリ ポイントからスローされたエラーは window.onerror によってキャッチされますが、window.onerror の完全な Error オブジェクトをサポートしていないブラウザでは、try/catch メソッドで言及されているため、これらのエラーをキャッチするには代替メカニズムが必要です。上記でもキャッチできません。
ありがたいことに、JavaScript ではこれらのエントリ ポイントをラップできるため、関数が呼び出される前に try/catch ブロックを挿入して、コードによってスローされたエラーをキャッチできるようになります。
エントリ ポイントを保護するには、各エントリ ポイントに若干異なるコードが必要になりますが、方法論の要点は次のとおりです。
関数protectEntryPoint(fn) { return function protectedFn() {try { return fn();} catch (e) { // エラーを処理します。} }}_oldSetTimeout = window.setTimeout;window.setTimeout = function protectedSetTimeout(fn, time) { return _oldSetTimeout.call(ウィンドウ、protectEntryPoint(fn)、時間);};
悲しいことに、Promise で発生したエラーは観察されず、報告されないままになることがよくあります。 Promise 内で発生しても、拒否ハンドラーのアタッチによって処理されないエラーは、他のどこにも報告されず、 window.onerror
にも報告されません。 Promise が拒否ハンドラーをアタッチした場合でも、そのコード自体がエラーをログに記録するために手動で報告する必要があります。詳細については、http://www.html5rocks.com/en/tutorials/es6/promises/#toc-error-handling を参照してください。例えば:
window.onerror = function(...) { // これは Promise コードによって呼び出されることはありません。};var p = new Promise(...);p.then(function() { throw new Error("このエラーはどこでも処理されません。");});var p2 = new Promise(...);p2.then(function() { throw new Error("このエラーはチェーンで処理されます。");}).catch(function(error) { // ユーザーにエラーメッセージを表示 // このコードは、該当する場合、サーバーに記録されるように手動でエラーを報告する必要があります。});
より多くの情報を取得するための 1 つのアプローチは、保護されたエントリ ポイントを使用して Promise メソッドの呼び出しを try/catch でラップし、エラーを報告することです。これは次のようになります。
var _oldPromiseThen = Promise.prototype.then; Promise.prototype.then = function protectedThen(callback, errorHandler) {return _oldPromiseThen.call(this,protectEntryPoint(callback),protectEntryPoint(errorHandler)); };
残念ながら、Promise からのエラーはデフォルトでは処理されません。
Q、Bluebird、Closure などの Promise 実装は、Promise のブラウザ実装でのエラー処理よりも優れたさまざまな方法でエラーを処理します。
Q では、 .done()
を呼び出すことで Promise チェーンを「終了」できます。これにより、エラーがチェーン内で処理されなかった場合、エラーが再スローされて報告されます。 https://github.com/kriskowal/q#handling-errors を参照してください。
Bluebird では、未処理の拒否がログに記録され、すぐに報告されます。 http://bluebirdjs.com/docs/features.html#surfacing-unhandled-errors を参照してください。
Closure の goog.Promise 実装では、構成可能な時間間隔内に Promise 内のチェーンが拒否を処理しない場合、未処理の拒否がログに記録され、報告されます (プログラムの後半のコードで拒否ハンドラーを追加できるようにするため)。
上記の非同期スタック トレース セクションでは、 Promise.prototype.then
の呼び出しなど、非同期フックがある場合にブラウザーがスタック情報をキャプチャしないことについて説明しています。 Promise のポリフィルには、エラーの診断をより簡単にできる非同期スタック トレース ポイントをキャプチャする方法が備わっています。このアプローチは高価ですが、より多くのデバッグ情報を取得するのに非常に役立ちます。
Q で、 Q.longStackSupport = true;
を呼び出します。 。 https://github.com/kriskowal/q#long-stack-traces を参照してください。
Bluebird では、アプリケーション内のどこかでPromise.longStackTraces()
を呼び出します。 http://bluebirdjs.com/docs/features.html#long-stack-traces を参照してください。
Closure で、 goog.Promise.LONG_STACK_TRACES
true に設定します。
Chrome 49 では、Promise が拒否されたときに送出されるイベントのサポートが追加されました。これにより、アプリケーションが Promise エラーにフックして、残りのエラーとともに確実に一元的に報告されるようになります。
window.addEventListener('unhandledrejection', イベント => { //event.reason には拒否理由が含まれます。エラーがスローされた場合、これは Error オブジェクトになります。});
詳細については、https://googlechrome.github.io/samples/promise-rejection-events/ および https://www.chromestatus.com/feature/4805872211460096 を参照してください。
これは他のブラウザではサポートされていません。
専用ワーカー、共有ワーカー、サービス ワーカーなどの Web ワーカーは、今日のアプリケーションでより一般的になりつつあります。これらのワーカーはすべてメイン ページとは別のスクリプトであるため、それぞれ独自のエラー処理コードが必要です。ワーカーからのエラーを最大限効率的に処理するために、各ワーカー スクリプトに独自のエラー処理およびレポート コードをインストールすることをお勧めします。
専用の Web ワーカーはメイン ページとは異なる実行コンテキストで実行されるため、ワーカーからのエラーは上記のメカニズムによって捕捉されません。ページ上のワーカーからのエラーをキャプチャするには、追加の手順を実行する必要があります。
ワーカーが作成されると、新しいワーカーに onerror プロパティを設定できます。
var worker = new Worker('worker.js');worker.onerror = function(errorEvent) { ... };
これは https://html.spec.whatwg.org/multipage/workers.html#handler-abstractworker-onerror で定義されています。ワーカーのonerror
関数には、上で説明したwindow.onerror
とは異なるシグネチャがあります。 worker.onerror
5 つの引数を受け入れる代わりに、 ErrorEvent
オブジェクトという 1 つの引数を受け取ります。このオブジェクトの API は、https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent にあります。これには、メッセージ、ファイル名、行、列が含まれていますが、スタック トレース (errorEvent.error は null) を含む「Error」オブジェクトを含む安定したブラウザは現在ありません。この API は親ページのスコープ内で実行されるため、親ページと同じレポート メカニズムを使用する場合に便利です。残念ながら、スタック トレースがないため、この API の用途は限られています。
ワーカーによって実行される JS 内で、通常の window.onerror API に従う onerror API を定義することもできます: https://html.spec.whatwg.org/multipage/webappapis.html#onerroreventhandler。ワーカーコードでは次のようになります。
self.onerror = function(メッセージ, ファイル名, 行, 列, エラー) { ... };
この API の説明は、主に、上記の window.onerror の説明に従います。ただし、注目すべき点が 2 つあります。
Firefox と Safari は、関数の 5 番目の引数として「error」オブジェクトを報告しないため、これらのブラウザーはワーカーからスタック トレースを取得しません (Chrome、MS Edge、および IE11 はスタック トレースを取得します)。ワーカー内のonmessage
関数の保護されたエントリ ポイントを使用して、これらのブラウザのスタック トレース情報をキャプチャできます。
このコードはワーカー内で実行されるため、コードはエラーをサーバーに報告する方法を選択する必要があります。postMessage postMessage
使用してエラーを親ページに伝えるか、XHR エラー報告メカニズム (以下で詳しく説明します) をインストールする必要があります。労働者そのもの。
Firefox、Safari、IE11 (Chrome では除く) では、ワーカー自身の onerror とページによって設定された onerror イベント リスナーが呼び出された後、親ページのwindow.onerror
関数も呼び出されます。ただし、この window.onerror にはエラー オブジェクトも含まれないため、スタック トレースもありません。これらのブラウザは、ワーカーからエラーを複数回報告しないように注意する必要もあります。
Chrome と Firefox は、複数のページ間でワーカーを共有するための SharedWorker API をサポートしています。ワーカーは共有されるため、1 つの親ページに排他的に関連付けられるわけではありません。これにより、エラーの処理方法にいくつかの違いが生じますが、SharedWorker はほとんどの場合、専用の Web ワーカーと同じ情報に従います。
Chrome では、SharedWorker でエラーが発生した場合、ワーカー コード自体内のワーカー独自のエラー処理のみが呼び出されます ( self.onerror
設定した場合など)。親ページのwindow.onerror
は呼び出されず、Chrome は、仕様で定義されているように親ページで呼び出すことができる継承されたAbstractWorker.onerror
をサポートしません。
Firefox では、この動作は異なります。共有ワーカーでエラーが発生すると、親ページの window.onerror が呼び出されますが、エラー オブジェクトは null になります。さらに、Firefox はAbstractWorker.onerror
プロパティをサポートしているため、親ページは独自のエラー ハンドラーをワーカーにアタッチできます。ただし、このエラー ハンドラーが呼び出されると、エラー オブジェクトは null になり、スタック トレースが存在しないため、用途は限定されます。
共有ワーカーのエラー処理はブラウザーによって異なります。
Service Worker は、現在 Chrome および Firefox の最新バージョンでのみ利用できるまったく新しい仕様です。これらのワーカーは、専任の Web ワーカーと同じディスカッションに従います。
Service Worker は、 navigator.serviceWorker.register
関数を呼び出すことによってインストールされます。この関数は、初期化中にエラーがスローされるなど、Service Worker のインストール中にエラーが発生した場合に拒否される Promise を返します。このエラーには文字列メッセージのみが含まれ、他には何も含まれません。さらに、Promise はwindow.onerror
ハンドラーにエラーを報告しないため、アプリケーション自体がエラーをキャッチするために catch ブロックを Promise に追加する必要があります。
navigator.serviceWorker.register('service-worker-installation-error.js').catch(function(error) { // 文字列のエラーの種類});
他のワーカーと同様に、Service Worker は Service Worker 内にself.onerror
関数を設定してエラーをキャッチできます。 Service Worker のインストール エラーは onerror 関数に報告されますが、残念ながらエラー オブジェクトやスタック トレースは含まれません。
Service Worker API には、AbstractWorker インターフェイスから継承された onerror プロパティが含まれていますが、Chrome は何も行いません