本自述文件包含我多年来学到的有关处理 JavaScript 错误、将错误报告给服务器以及解决许多可能使这一切变得非常困难的错误的信息。浏览器在这方面已经有所改进,但仍然有改进的空间,以确保所有应用程序都能理智、健全地处理发生的任何错误。
本指南中内容的测试用例可以在 https://mknichel.github.io/javascript-errors/ 中找到。
目录
介绍
JavaScript 错误剖析
产生 JavaScript 错误
错误信息
堆栈跟踪格式
捕获 JavaScript 错误
窗口错误
尝试/捕捉
受保护的入口点
承诺
网络工作者
Chrome 扩展程序
捕获、报告和修复错误是任何应用程序的重要组成部分,以确保应用程序的健康和稳定性。由于 JavaScript 代码也在客户端和许多不同的浏览器环境中执行,因此掌握应用程序中的 JS 错误也可能很困难。没有关于如何报告 JS 错误的正式 Web 规范,这会导致每个浏览器的实现存在差异。此外,浏览器的 JavaScript 错误实现中也存在许多错误,这使得这变得更加困难。本页面将介绍 JS 错误的这些方面,以便未来的开发人员能够更好地处理错误,并且浏览器有望收敛于标准化解决方案。
JavaScript 错误由两个主要部分组成:错误消息和堆栈跟踪。错误消息是一个字符串,描述出了什么问题,堆栈跟踪描述了代码中发生错误的位置。 JS 错误可以由浏览器本身产生,也可以由应用程序代码引发。
当一段代码没有正确执行时,浏览器可能会抛出JS错误,也可能由代码直接抛出。
例如:
var a = 3;a();
在此示例中,实际上是数字的变量不能作为函数调用。浏览器将抛出类似TypeError: a is not a function
with a stack trace that point of that line of code 的错误。
如果不满足某个先决条件,开发人员可能还想在一段代码中抛出错误。例如
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
语句中存在多个 default 语句时,Chrome 会抛出"More than one default clause in switch statement"
而 Firefox 则会报告"more than one switch default"
。随着新功能添加到网络中,这些错误消息必须更新。当您尝试处理混淆代码报告的错误时,这些差异可能会在稍后发挥作用。
您可以在以下位置找到浏览器用于错误消息的模板:
火狐 - 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(函数名OfTheAnonymousFunction() { ... }, 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 = 函数() { ... };
浏览器将使用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 除外,它将使用不同的形式来连接变量的名称外部函数与内部变量的名称。例子:
函数 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 -javascript-profiler-with-webkit/ 了解有关 displayName 的更多信息。
IE11 不支持 displayName 属性。
Safari 使用 displayName 属性作为错误堆栈跟踪中的符号名称。
如果报告错误时没有堆栈跟踪(请参阅下面发生这种情况的更多详细信息),则可以以编程方式捕获堆栈跟踪。
在 Chrome 中,通过使用Error.captureStackTrace
API 可以很容易地做到这一点。有关使用此 API 的更多信息,请参阅 https://github.com/v8/v8/wiki/Stack%20Trace%20API。
例如:
函数ignoreThisFunctionInStackTrace() { var err = 新错误(); Error.captureStackTrace(err,ignoreThisFunctionInStackTrace); 返回 err.stack;}
在其他浏览器中,还可以通过创建新错误并访问该对象的堆栈属性来收集堆栈跟踪:
var err = new Error('');返回 err.stack;
但是,IE10 仅在实际引发错误时填充堆栈跟踪:
尝试 { 抛出新的错误('');} catch (e) { 返回 e.stack;}
如果这些方法都不起作用,那么可以通过迭代arguments.callee.caller
对象来创建没有行号或列的粗略堆栈跟踪——但这在ES5严格模式下不起作用,并且不是推荐的方法。
将异步点插入到 JavaScript 代码中是很常见的,例如当代码使用setTimeout
或通过使用 Promises 时。这些异步入口点可能会导致堆栈跟踪出现问题,因为它们会导致形成新的执行上下文,并且堆栈跟踪再次从头开始。
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 文档的开头来计算行号和列号。对于与上面相同的错误,使用值为“inline.js”的 sourceURL 注释将生成如下所示的堆栈跟踪:
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)
在 Safari 中:
Error from eval evaledFunction eval code eval@[native code] evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:7
在火狐浏览器中:
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 代码。
每个浏览器对 eval 内发生的错误使用不同的堆栈跟踪格式。
您的 JavaScript 代码也可以直接从本机代码调用。 Array.prototype.forEach
是一个很好的例子 - 你将一个函数传递给forEach
,JS 引擎将为你调用该函数。
函数 throwErrorWithNativeFrame() { var arr = [0, 1, 2, 3]; arr.forEach(函数namedFn(value) { 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
是开始捕获错误的最简单、最好的方法之一。通过将window.onerror
分配给一个函数,应用程序的其他部分未捕获的任何错误都将报告给该函数,以及有关该错误的一些信息。例如:
window.onerror = 函数(msg, url, line, col, err) { console.log('应用程序遇到错误:' + msg); console.log('堆栈跟踪:' + err.stack);}
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror 更详细地描述了这一点。
从历史上看,这种方法存在一些问题:
没有提供错误对象
window.onerror
函数的第五个参数应该是一个 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 处理程序中另一个域的错误将被清理为“脚本错误。”、“”、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 块以捕获代码引发的任何错误。
每个入口点需要稍微不同的代码来保护入口点,但该方法的要点是:
函数保护入口点(fn){ return function protectedFn() {try { return fn();} catch (e) { // 处理错误。} }}_oldSetTimeout = window.setTimeout;window.setTimeout = 函数 protectedSetTimeout(fn, time) { 返回_oldSetTimeout.call(窗口,protectEntryPoint(fn),时间);};
遗憾的是,Promise 中发生的错误很容易被忽视和报告。 Promise 中发生但未通过附加拒绝处理程序处理的错误不会在其他任何地方报告 - 它们不会报告给window.onerror
。即使 Promise 附加了拒绝处理程序,该代码本身也必须手动报告这些错误才能记录它们。请参阅 http://www.html5rocks.com/en/tutorials/es6/promises/#toc-error-handling 了解更多信息。例如:
window.onerror = 函数(...) { // 这永远不会被 Promise 代码调用。};var p = new Promise(...);p.then(function() { throw new Error("此错误将不会在任何地方处理。");});var p2 = new Promise(...);p2.then(function() { throw new Error("这个错误将在链中处理。");}).catch(function(error) { // 向用户显示错误信息 // 此代码应手动报告错误,以便将其记录在服务器上(如果适用)。});
捕获更多信息的一种方法是使用受保护的入口点通过 try/catch 来包装 Promise 方法的调用以报告错误。这可能看起来像:
var _oldPromiseThen = Promise.prototype.then; Promise.prototype.then = function protectedThen(callback, errorHandler) {return _oldPromiseThen.call(this,protectEntryPoint(callback),protectEntryPoint(errorHandler)); };
遗憾的是,Promise 中的错误默认情况下不会被处理。
Promise 实现(例如 Q、Bluebird 和 Closure)以不同的方式处理错误,这比 Promise 的浏览器实现中的错误处理更好。
在 Q 中,您可以通过调用.done()
来“结束”Promise 链,这将确保如果链中未处理错误,它将被重新抛出并报告。请参阅 https://github.com/kriskowal/q#handling-errors
在 Bluebird 中,未处理的拒绝会被立即记录并报告。请参阅http://bluebirdjs.com/docs/features.html#surface-unhandled-errors
在 Closure 的 goog.Promise 实现中,如果 Promise 中没有链在可配置的时间间隔内处理拒绝,则会记录和报告未处理的拒绝(以便允许程序中稍后的代码添加拒绝处理程序)。
上面的异步堆栈跟踪部分讨论了当存在异步挂钩(例如调用Promise.prototype.then
)时,浏览器不会捕获堆栈信息。 Promise polyfill 提供了一种捕获异步堆栈跟踪点的方法,这可以使诊断错误变得更加容易。这种方法很昂贵,但它对于捕获更多调试信息非常有用。
在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 => { // event.reason 包含拒绝原因。当抛出错误时,这是 Error 对象。});
有关详细信息,请参阅 https://googlechrome.github.io/samples/promise-rejection-events/ 和 https://www.chromestatus.com/feature/4805872211460096。
任何其他浏览器都不支持此功能。
Web Worker,包括专用 Worker、共享 Worker 和服务 Worker,在当今的应用程序中变得越来越流行。由于所有这些工作人员都是与主页分开的脚本,因此它们每个都需要自己的错误处理代码。建议每个工作脚本安装自己的错误处理和报告代码,以最大程度地有效处理工作人员的错误。
专用 Web Worker 在与主页不同的执行上下文中执行,因此上述机制不会捕获来自 Worker 的错误。需要采取额外的步骤来捕获页面上工作人员的错误。
创建worker时,可以在新worker上设置onerror属性:
var worker = new Worker('worker.js');worker.onerror = function(errorEvent) { ... };
这是在 https://html.spec.whatwg.org/multipage/workers.html#handler-abstractworker-onerror 中定义的。 Worker 上的onerror
函数具有与上面讨论的window.onerror
不同的签名。 worker.onerror
不接受 5 个参数,而是采用单个参数: ErrorEvent
对象。该对象的 API 可以在 https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent 找到。它包含消息、文件名、行和列,但目前没有稳定的浏览器包含包含堆栈跟踪的“Error”对象(errorEvent.error 为 null)。由于此 API 是在父页面的范围内执行的,因此使用与父页面相同的报告机制将很有用;不幸的是,由于缺乏堆栈跟踪,该 API 的用途有限。
在工作线程运行的 JS 内部,您还可以定义一个遵循常见 window.onerror API 的 onerror API:https://html.spec.whatwg.org/multipage/webappapis.html#onerroreventhandler。在工人代码中:
self.onerror = 函数(消息, 文件名, 行, 列, 错误) { ... };
该 API 的讨论主要遵循上面针对 window.onerror 的讨论。不过,有两点值得注意:
Firefox 和 Safari 不会将“错误”对象报告为函数的第五个参数,因此这些浏览器不会从工作线程获取堆栈跟踪(Chrome、MS Edge 和 IE11 确实会获取堆栈跟踪)。工作线程中onmessage
函数的受保护入口点可用于捕获这些浏览器的堆栈跟踪信息。
由于此代码在工作程序中执行,因此代码必须选择如何将错误报告回服务器:它必须使用postMessage
将错误传达回父页面,或者安装 XHR 错误报告机制(下面将详细讨论)工人本身。
在 Firefox、Safari 和 IE11 中(但在 Chrome 中则不然),在调用了 Worker 自己的 onerror 和页面设置的 onerror 事件监听器之后,父页面的window.onerror
函数也会被调用。但是,此 window.onerror 也不会包含错误对象,因此也不会具有堆栈跟踪。这些浏览器还必须注意不要多次报告工作人员的错误。
Chrome 和 Firefox 支持 SharedWorker API,用于在多个页面之间共享工作线程。由于工作线程是共享的,因此它不会专门附加到一个父页面;尽管 SharedWorker 大多遵循与专用 Web Worker 相同的信息,但这会导致错误处理方式存在一些差异。
在 Chrome 中,当 SharedWorker 中出现错误时,只会调用工作人员代码本身内的工作人员自己的错误处理(就像他们设置self.onerror
一样)。父页面的window.onerror
不会被调用,并且Chrome不支持规范中定义的继承的可以在父页面中调用的AbstractWorker.onerror
。
在 Firefox 中,这种行为有所不同。共享worker中的错误将导致父页面的window.onerror被调用,但错误对象将为null。此外,Firefox 确实支持AbstractWorker.onerror
属性,因此父页面可以将自己的错误处理程序附加到工作线程。但是,当调用此错误处理程序时,错误对象将为 null,因此不会有堆栈跟踪,因此它的用途有限。
共享工作线程的错误处理因浏览器而异。
Service Workers 是一个全新的规范,目前仅在最新的 Chrome 和 Firefox 版本中可用。这些工作人员遵循与专门的网络工作人员相同的讨论。
Service Worker是通过调用navigator.serviceWorker.register
函数来安装的。该函数返回一个 Promise,如果安装 Service Worker 时出现错误(例如在初始化期间抛出错误),该 Promise 将被拒绝。此错误将只包含字符串消息,不包含任何其他内容。此外,由于 Promise 不会向window.onerror
处理程序报告错误,因此应用程序本身必须向 Promise 添加一个 catch 块来捕获错误。
navigator.serviceWorker.register('service-worker-installation-error.js').catch(function(error) { // 字符串类型错误});
就像其他工作人员一样,服务工作人员可以在服务工作人员中设置self.onerror
函数来捕获错误。 Service Worker 中的安装错误将报告给 onerror 函数,但不幸的是它们不会包含错误对象或堆栈跟踪。
Service Worker API 包含从 AbstractWorker 接口继承的 onerror 属性,但 Chrome 不执行任何操作