JavaScript のエラー処理メソッド

小云云
小云云オリジナル
2018-01-26 10:15:132469ブラウズ

JavaScript のイベント駆動型パラダイムは、言語に豊かさを加え、JavaScript を使用したプログラミングをより多様なものにします。ブラウザを JavaScript のイベント駆動型ツールと考えると、エラーが発生するとイベントがスローされます。理論的には、これらのエラーは JavaScript の単純なイベントであると考えることができます。

この記事では、クライアントサイド JavaScript でのエラー処理について説明します。主にJavaScriptでよくある間違いやエラー処理、非同期コードの書き方などを紹介します。

JavaScript でエラーを正しく処理する方法を見てみましょう。

デモデモ

この記事で使用されているデモは GitHub にあります。実行後、ページは次のようになります:

各ボタンは「エラー (例外)」をトリガーし、このエラーがシミュレートされます。例外 TypeError がスローされました。モジュール定義は次のとおりです:

// scripts/error.js
function error() {
 var foo = {};
 return foo.bar();
}

まず、この関数は空のオブジェクト foo を宣言します。 bar() はどこにも定義されていないことに注意してください。次に、この単体テストが「エラー」をスローするかどうかを確認します。

// tests/scripts/errorTest.js
it('throws a TypeError', function () {
 should.throws(error, TypeError);
});

この単体テストは Mocha にあり、Should.js にテスト宣言があります。 Mocha はテスト ランナー ツールで、Should.js はアサーション ライブラリです。この単体テストはノード上で実行され、ブラウザを使用する必要はありません。

error() は空のオブジェクトを定義し、メソッドにアクセスしようとします。 bar() がオブジェクト内に存在しないため、例外がスローされます。 JavaScript のような動的言語で発生するこのエラーは、誰にでも起こる可能性があります。

エラー処理 (1)

上記のエラーを処理するには、次のコードを使用します。

// scripts/badHandler.js
function badHandler(fn) {
 try {
  return fn();
 } catch (e) { }
 return null;
}

ハンドラーは fn を入力パラメーターとして受け取り、ハンドラー関数内で fn が呼び出されます。単体テストには、上記のエラー ハンドラーの役割が反映されます:

// tests/scripts/badHandlerTest.js
it('returns a value without errors', function() {
 var fn = function() {
  return 1;
 };
 var result = badHandler(fn);
 result.should.equal(1);
});

it('returns a null with errors', function() {
 var fn = function() {
  throw new Error('random error');
 };
 var result = badHandler(fn);
 should(result).equal(null);
});

何か問題が発生した場合、エラー ハンドラーは null を返します。 fn() コールバック関数は、正当なメソッドまたはエラーを指すことができます。

次のクリックイベントはイベント処理を継続します:

// scripts/badHandlerDom.js
(function (handler, bomb) {
 var badButton = document.getElementById('bad');
 if (badButton) {
  badButton.addEventListener('click', function () {
   handler(bomb);
   console.log('Imagine, getting promoted for hiding mistakes');
  });
 }
}(badHandler, error));

この処理メソッドはコード内のエラーを隠すため、見つけるのが困難です。隠れたエラーのデバッグには何時間もかかる場合があります。特にコールスタックが深いマルチレイヤーソリューションでは、このエラーを見つけるのが難しくなる可能性があります。したがって、これはエラーを処理する非常に不適切な方法です。

エラー処理(2)

もう一つのエラー処理方法は以下の通りです。

// scripts/uglyHandler.js
function uglyHandler(fn) {
 try {
  return fn();
 } catch (e) {
  throw new Error('a new error');
 }
}

例外が処理される方法は次のとおりです:

// tests/scripts/uglyHandlerTest.js
it('returns a new error with errors', function () {
 var fn = function () {
  throw new TypeError('type error');
 };
 should.throws(function () {
  uglyHandler(fn);
 }, Error);
});

上記は、エラー ハンドラーの大幅な改善です。ここで、例外により呼び出しスタックがバブルアップされます。同時に、エラーによってスタックが拡張されるため、デバッグに非常に役立ちます。例外をスローすることに加えて、インタプリタはスタックに沿って追加の処理を探します。これにより、スタックの最上位からエラーが処理される可能性も生じます。しかし、これは依然として不十分なエラー処理であり、元の例外をスタックの下に段階的に追跡する必要があります。

カスタム エラー メソッドを使用して、この不適切なエラー処理を終了する代替手段を採用できます。このアプローチは、エラーに詳細を追加するときに役立ちます。

例:

// scripts/specifiedError.js
// Create a custom error
var SpecifiedError = function SpecifiedError(message) {
 this.name = 'SpecifiedError';
 this.message = message || '';
 this.stack = (new Error()).stack;
};
SpecifiedError.prototype = new Error();
SpecifiedError.prototype.constructor = SpecifiedError;
// scripts/uglyHandlerImproved.js
function uglyHandlerImproved(fn) {
 try {
  return fn();
 } catch (e) {
  throw new SpecifiedError(e.message);
 }
}
// tests/scripts/uglyHandlerImprovedTest.js
it('returns a specified error with errors', function () {
 var fn = function () {
  throw new TypeError('type error');
 };
 should.throws(function () {
  uglyHandlerImproved(fn);
 }, SpecifiedError);
});

指定されたエラーには詳細が追加され、元のエラー メッセージが保持されます。この改良により、上記の処理は下手な処理方法ではなく、明確で有用な処理方法となりました。

上記の処理後、ハンドルされない例外も受け取りました。次に、エラー処理時にブラウザがどのように役立つかを見てみましょう。

スタックを拡張する

例外を処理する 1 つの方法は、呼び出しスタックの先頭に try...catch を追加することです。

例:

function main(bomb) {
 try {
  bomb();
 } catch (e) {
  // Handle all the error things
 }
}

ただし、ブラウザはイベント駆動型であり、JavaScript の例外もイベントです。例外が発生すると、インタープリターは実行を一時停止し、巻き戻します。

// scripts/errorHandlerDom.js
window.addEventListener('error', function (e) {
 var error = e.error;
 console.log(error);
});

このイベント ハンドラーは、実行コンテキストで発生するエラーをキャッチします。さまざまなターゲットで発生するエラー イベントは、さまざまなタイプのエラーを引き起こす可能性があります。コード内でのこの集中エラー処理は非常に積極的です。デイジーチェーンを使用して特定のエラーを処理できます。 SOLID 原則に従えば、単一の目的でエラー処理を使用できます。これらのハンドラーはいつでも登録でき、インタープリターは実行する必要のあるハンドラーをループします。コード ベースは try...catch ブロックから解放できるため、デバッグも簡単になります。 JavaScript では、エラー処理をイベント処理として扱うことが重要です。

スタックのキャプチャ

呼び出しスタックは問題を解決するときに非常に役立ち、ブラウザーはこの情報を提供できます。スタック属性は標準の一部ではありませんが、最近のブラウザではすでにこの情報を表示できます。

サーバー上のエラーのログ記録の例を次に示します:

// scripts/errorAjaxHandlerDom.js
window.addEventListener('error', function (e) {
 var stack = e.error.stack;
 var message = e.error.toString();
 if (stack) {
  message += '\n' + stack;
 }
 var xhr = new XMLHttpRequest();
 xhr.open('POST', '/log', true);
 // Fire an Ajax request with error details
 xhr.send(message);
});

各エラー処理には単一の目的があり、コードの DRY 原則 (単一の目的、同じことを繰り返さない原則) が維持されます。

ブラウザでは、イベント処理を DOM に追加する必要があります。これは、サードパーティのライブラリを構築している場合、イベントがクライアント コードと共存することを意味します。 window.addEventListener() は、既存のイベントを消去せずにそれを処理します。

これはサーバー上のログのスクリーンショットです:

可以通过命令提示符查看日志,但是Windows上,日志是非动态的。

通过日志可以清楚的看到,具体什么情况触发了什么错误。在调试时调用堆栈也会非常有用,所以不要低估调用堆栈的作用。

在JavaScript中,错误信息仅适用于单个域。因为在使用来自不用域的脚本时,将会看不到任何错误详细信息。

一种解决方案是重新抛出错误,同时保留错误消息:

一旦重新启动了错误备

try {
 return fn();
} catch (e) {
 throw new Error(e.message);
}

份,全局错误处理程序就会完成其余的工作。确保你的错误处理处在相同域中,这样会保留原始消息,堆栈和自定义错误对象。

异步处理

JavaScript在运行异步代码时,进行下面的异常处理,会产生一个问题:

// scripts/asyncHandler.js
function asyncHandler(fn) {
 try {
  // This rips the potential bomb from the current context
  setTimeout(function () {
   fn();
  }, 1);
 } catch (e) { }
}

通过单元测试来查看问题:

// tests/scripts/asyncHandlerTest.js
it('does not catch exceptions with errors', function () {
 // The bomb
 var fn = function () {
  throw new TypeError('type error');
 };
 // Check that the exception is not caught
 should.doesNotThrow(function () {
  asyncHandler(fn);
 });
});

这个异常没有被捕获,我们通过单元测试来验证。尽管代码包含了try...catch,但是try...catch语句只能在单个执行上下文中工作。当异常被抛出时,解释器已经脱离了try...catch,所以异常未被处理。Ajax调用也会发生同样的情况。

所以,一种解决方案是在异步回调中捕获异常:

setTimeout(function () {
 try {
  fn();
 } catch (e) {
  // Handle this async error
 }
}, 1);

这种做法会比较奏效,但仍有很大的改进空间。

首先,这些try...catch block在整个区域纠缠不清。事实上,V8浏览器引擎不鼓励在函数内使用try ... catch block。V8是Chrome浏览器和Node中使用的JavaScript引擎。一种做法是将try...catch block移动到调用堆栈的顶部,但这却不适用于异步代码编程。

由于全局错误处理可以在任何上下文中执行,所以如果为错误处理添加一个窗口对象,那么就能保证代码的DRY和SOLID原则。同时全局错误处理也能保证你的异步代码很干净。

以下是该异常处理在服务器上的报告内容。请注意,输出内容会根据浏览器的不同而不同。

从错误处理中可以看到,错误来自于异步代码的setTimeout( )功能。

结论

在进行错误处理时,不要隐藏问题,而应该及时发现问题,并采用各种方法追溯问题的根源以便解决问题。虽然编写代码时,时常难免会埋下错误,但是我们也无须为错误的发生过于感到羞愧,及时解决发现问题从而避免更大的问题发生,正是我们现在需要做的。

相关推荐:

jQuery出错与解决方法小结

以上がJavaScript のエラー処理メソッドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。