Este README contiene información que he aprendido a lo largo de los años sobre cómo lidiar con errores de JavaScript, informarlos al servidor y navegar a través de muchos errores que pueden hacer que todo esto sea realmente difícil. Los navegadores han mejorado en esta área, pero aún queda margen de mejora para garantizar que todas las aplicaciones puedan manejar de manera sensata y sólida cualquier error que ocurra.
Los casos de prueba del contenido que se encuentra en esta guía se pueden encontrar en https://mknichel.github.io/javascript-errors/.
Tabla de contenido
Introducción
Anatomía de un error de JavaScript
Produciendo un error de JavaScript
Mensajes de error
Formato de seguimiento de pila
Detectar errores de JavaScript
ventana.onerror
probar/atrapar
Puntos de entrada protegidos
Promesas
Trabajadores web
Extensiones de Chrome
Detectar, informar y corregir errores es una parte importante de cualquier aplicación para garantizar el estado y la estabilidad de la aplicación. Dado que el código JavaScript también se ejecuta en el cliente y en muchos entornos de navegador diferentes, estar al tanto de los errores JS de su aplicación también puede resultar difícil. No existen especificaciones web formales sobre cómo informar errores de JS que causan diferencias en la implementación de cada navegador. Además, ha habido muchos errores en la implementación de errores de JavaScript en los navegadores que han hecho que esto sea aún más difícil. Esta página explora estos aspectos de los errores de JS para que los futuros desarrolladores puedan manejar mejor los errores y, con suerte, los navegadores converjan en soluciones estandarizadas.
Un error de JavaScript se compone de dos partes principales: el mensaje de error y el seguimiento de la pila . El mensaje de error es una cadena que describe lo que salió mal y el seguimiento de la pila describe en qué parte del código ocurrió el error. Los errores JS pueden ser producidos por el propio navegador o por el código de la aplicación.
El navegador puede generar un error JS cuando un fragmento de código no se ejecuta correctamente, o puede generarlo directamente mediante código.
Por ejemplo:
var a = 3;a();
En este ejemplo, una variable que en realidad es un número no se puede invocar como función. El navegador arrojará un error como TypeError: a is not a function
con un seguimiento de pila que apunte a esa línea de código.
Es posible que un desarrollador también desee generar un error en un fragmento de código si no se cumple una determinada condición previa. Por ejemplo
si (!checkPrecondition()) { throw new Error("¡No cumple con la condición previa!");}
En este caso, el error será Error: Doesn't meet precondition!
. Este error también contendrá un seguimiento de la pila que apunta a la línea apropiada. Los errores generados por el navegador y el código de la aplicación se pueden manejar de la misma manera.
Hay varias formas en que los desarrolladores pueden generar un error en JavaScript:
throw new Error('Problem description.')
throw Error('Problem description.')
<-- equivalente al primero
throw 'Problem description.'
<-- malo
throw null
<-- incluso peor
Realmente no se recomienda arrojar una cadena o un valor nulo, ya que el navegador no adjuntará un seguimiento de la pila a ese error, perdiendo el contexto de dónde ocurrió ese error en el código. Es mejor generar un objeto Error real, que contendrá el mensaje de error así como un seguimiento de la pila que apunte a las líneas correctas de código donde ocurrió el error.
Cada navegador tiene su propio conjunto de mensajes que utiliza para las excepciones integradas, como el ejemplo anterior para intentar llamar a una función que no es. Los navegadores intentarán utilizar los mismos mensajes, pero como no hay ninguna especificación, esto no está garantizado. Por ejemplo, tanto Chrome como Firefox usan {0} is not a function
para el ejemplo anterior, mientras que IE11 informará Function expected
(en particular, también sin informar qué variable se intentó llamar).
Sin embargo, los navegadores también tienden a divergir con frecuencia. Cuando hay varias declaraciones predeterminadas en una declaración switch
, Chrome arrojará "More than one default clause in switch statement"
mientras que Firefox informará "more than one switch default"
. A medida que se agregan nuevas funciones a la web, estos mensajes de error deben actualizarse. Estas diferencias pueden entrar en juego más adelante cuando intente gestionar los errores informados por código ofuscado.
Puede encontrar las plantillas que utilizan los navegadores para los mensajes de error en:
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
Los navegadores producirán diferentes mensajes de error para algunas excepciones.
El seguimiento de la pila es una descripción de dónde ocurrió el error en el código. Se compone de una serie de cuadros, donde cada cuadro describe una línea particular en el código. El fotograma superior es la ubicación donde se produjo el error, mientras que los fotogramas siguientes son la pila de llamadas a la función, o cómo se ejecutó el código para llegar al punto donde se produjo el error. Dado que JavaScript suele estar concatenado y minimizado, también es importante tener números de columna para que se pueda localizar la declaración exacta cuando una línea determinada tenga una multitud de declaraciones.
Un seguimiento de pila básico en Chrome se ve así:
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
Cada marco de pila consta de un nombre de función (si corresponde y el código no se ejecutó en el ámbito global), el script del que proviene y el número de línea y columna del código.
Desafortunadamente, no existe un estándar para el formato de seguimiento de la pila, por lo que difiere según el navegador.
El seguimiento de la pila de Microsoft Edge e IE 11 es similar al de Chrome, excepto que enumera explícitamente el código global:
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)
El seguimiento de la pila de Firefox se ve así:
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
El formato de Safari es similar al formato de Firefox pero también es ligeramente diferente:
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
Allí se encuentra la misma información básica, pero el formato es diferente.
También tenga en cuenta que en el ejemplo de Safari, además de que el formato es diferente al de Chrome, los números de columna son diferentes a los de Chrome y Firefox. Los números de las columnas también pueden desviarse más en diferentes situaciones de error, por ejemplo en el código (function namedFunction() { throwError(); })();
, Chrome informará la columna para la llamada a la función throwError()
mientras que IE11 informará el número de columna como el inicio de la cadena. Estas diferencias volverán a entrar en juego más adelante, cuando el servidor necesite analizar el seguimiento de la pila en busca de errores informados y desofuscar los seguimientos de la pila ofuscados.
Consulte https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack para obtener más información sobre la propiedad de pila de los errores. Al acceder a la propiedad Error.stack, Chrome incluye el mensaje de error como parte de la pila, pero Safari 10+ no.
El formato de los seguimientos de la pila difiere según el navegador en cuanto a la forma y los números de columna utilizados.
Profundizando más, hay muchos matices para apilar formatos de seguimiento que se analizan en las secciones siguientes.
De forma predeterminada, las funciones anónimas no tienen nombre y aparecen como una cadena vacía o como "Función anónima" en los nombres de las funciones en el seguimiento de la pila (según el navegador). Para mejorar la depuración, debe agregar un nombre a todas las funciones para asegurarse de que aparezcan en el marco de la pila. La forma más sencilla de hacerlo es asegurarse de que las funciones anónimas se especifiquen con un nombre, incluso si ese nombre no se utiliza en ningún otro lugar. Por ejemplo:
setTimeout(nombre de funciónOfTheAnonymousFunction() {... }, 0);
Esto hará que el seguimiento de la pila vaya desde:
at http://mknichel.github.io/javascript-errors/javascript-errors.js:125:17
a
at nameOfTheAnonymousFunction (http://mknichel.github.io/javascript-errors/javascript-errors.js:121:31)
En Safari, esto iría desde:
https://mknichel.github.io/javascript-errors/javascript-errors.js:175:27
a
nameOfTheAnonymousFunction@https://mknichel.github.io/javascript-errors/javascript-errors.js:171:41
Este método garantiza que nameOfTheAnonymousFunction
aparezca en el marco para cualquier código dentro de esa función, lo que facilita mucho la depuración. Consulte http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/#toc-debugging-tips para obtener más información.
Los navegadores también usarán el nombre de la variable o propiedad a la que está asignada una función si la función en sí no tiene un nombre. Por ejemplo, en
var fnVariableName = función() {...};
Los navegadores utilizarán fnVariableName
como nombre de la función en los seguimientos de la pila.
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)
Aún más matizado que eso, si esta variable se define dentro de otra función, todos los navegadores usarán solo el nombre de la variable como nombre de la función en el seguimiento de la pila, excepto Firefox, que usará una forma diferente que concatena el nombre de la función externa con el nombre de la variable interna. Ejemplo:
función throwErrorFromInnerFunctionAssignedToVariable() { var fnVariableName = función() { lanzar nuevo Error("foo"); }; fnNombreVariable();}
producirá en Firefox:
throwErrorFromInnerFunctionAssignedToVariable/fnVariableName@http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37
En otros navegadores, esto se vería así:
at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
Firefox utiliza texto de marco de pila diferente para funciones definidas dentro de otra función.
El nombre para mostrar de una función también se puede establecer mediante la propiedad displayName
en todos los navegadores principales, excepto IE11. En estos navegadores, el nombre para mostrar aparecerá en el depurador de devtools, pero en todos los navegadores excepto Safari, no se usará en los seguimientos de la pila de errores (Safari se diferencia del resto porque también usa el nombre para mostrar en el seguimiento de la pila asociado con un error).
var someFunction = function() {};someFunction.displayName = " # Una descripción más larga de la función.";
No existe una especificación oficial para la propiedad displayName, pero es compatible con los principales navegadores. Consulte https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName y http://www.alertdebugging.com/2009/04/29/building-a-better -javascript-profiler-with-webkit/ para obtener más información sobre displayName.
IE11 no admite la propiedad displayName.
Safari usa la propiedad displayName como nombre del símbolo en los seguimientos de la pila de errores.
Si se informa un error sin un seguimiento de la pila (consulte más detalles sobre cuándo sucedería esto a continuación), entonces es posible capturar un seguimiento de la pila mediante programación.
En Chrome, esto es realmente fácil de hacer usando la API Error.captureStackTrace
. Consulte https://github.com/v8/v8/wiki/Stack%20Trace%20API para obtener más información sobre el uso de esta API.
Por ejemplo:
función ignorarEstaFunciónInStackTrace() { var err = nuevo Error(); Error.captureStackTrace (err, ignoreThisFunctionInStackTrace); devolver error.pila;}
En otros navegadores, también se puede recopilar un seguimiento de la pila creando un nuevo error y accediendo a la propiedad de la pila de ese objeto:
var err = nuevo Error('');return err.stack;
Sin embargo, IE10 solo completa el seguimiento de la pila cuando realmente se produce el error:
intentar { lanzar nuevo Error('');} catch (e) { devolver e.stack;}
Si ninguno de estos enfoques funciona, entonces es posible crear un seguimiento de pila aproximado sin números de línea o columnas iterando sobre el objeto arguments.callee.caller
; sin embargo, esto no funcionará en el modo estricto de ES5 y no es un enfoque recomendado.
Es muy común que se inserten puntos asincrónicos en el código JavaScript, como cuando el código usa setTimeout
o mediante el uso de Promesas. Estos puntos de entrada asíncronos pueden causar problemas para los seguimientos de la pila, ya que provocan que se forme un nuevo contexto de ejecución y el seguimiento de la pila comienza desde cero nuevamente.
Chrome DevTools admite seguimientos de pila asíncronos o, en otras palabras, asegurarse de que el seguimiento de pila de un error también muestre los fotogramas que ocurrieron antes de que se introdujera el punto asíncrono. Con el uso de setTimeout, esto capturará quién llamó a la función setTimeout que finalmente produjo un error. Consulte http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/ para obtener más información.
Un seguimiento de pila asíncrono se verá así:
throwError @ throw-error.js:2 setTimeout (async) throwErrorAsync @ throw-error.js:10 (anonymous function) @ throw-error-basic.html:14
Los seguimientos de pila asíncronos solo se admiten en Chrome DevTools en este momento, solo para las excepciones que se generan cuando DevTools está abierto. Los seguimientos de pila a los que se accede desde objetos Error en el código no tendrán el seguimiento de pila asíncrono como parte del mismo.
Es posible realizar un polirrelleno de seguimientos de pila asíncronos en algunos casos, pero esto podría causar un impacto significativo en el rendimiento de su aplicación, ya que capturar un seguimiento de pila no es barato.
Solo Chrome DevTools admite de forma nativa seguimientos de pila asíncronos.
Los seguimientos de pila para el código que fue evaluado o insertado en una página HTML utilizarán la URL de la página y los números de línea/columna para el código ejecutado.
Por ejemplo:
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
Si estos scripts en realidad provienen de un script que se incluyó por motivos de optimización, entonces la URL, los números de línea y de columna serán incorrectos. Para solucionar este problema, Chrome y Firefox admiten la anotación //# sourceURL=
(Safari, Edge e IE no). La URL especificada en esta anotación se utilizará como URL para todos los seguimientos de la pila, y el número de línea y columna se calculará en relación con el inicio de la etiqueta en lugar del documento HTML. Para el mismo error anterior, usar la anotación sourceURL con un valor de "inline.js" producirá un seguimiento de pila similar a:
at throwError (http://mknichel.github.io/javascript-errors/inline.js:8:9) at http://mknichel.github.io/javascript-errors/inline.js:12:3
Esta es una técnica realmente útil para asegurarse de que los seguimientos de la pila sigan siendo correctos incluso cuando se utilizan scripts en línea y evaluación.
http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl describe la anotación sourceURL con más detalle.
Safari, Edge e IE no admiten la anotación sourceURL para nombrar scripts y evaluaciones en línea. Si utiliza scripts en línea en IE o Safari y ofusca su código, no podrá desofuscar los errores que provienen de esos scripts.
Hasta Chrome 42, Chrome no calculaba correctamente los números de línea para los scripts en línea que usaban la anotación sourceURL. Consulte https://bugs.chromium.org/p/v8/issues/detail?id=3920 para obtener más información.
Los números de línea para los marcos de pila de los scripts en línea son incorrectos cuando se utiliza la anotación sourceURL, ya que son relativos al inicio del documento HTML en lugar del inicio de la etiqueta del script en línea (lo que hace que no sea posible una desofuscación correcta). https://code.google.com/p/chromium/issues/detail?id=578269
Para el código que usa eval, existen otras diferencias en el seguimiento de la pila además de si usa o no la anotación sourceURL. En Chrome, un seguimiento de pila de una declaración utilizada en eval podría verse así:
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)
En MS Edge e IE11, esto se vería así:
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)
En Safari:
Error from eval evaledFunction eval code eval@[native code] evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:7
y en 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
Estas diferencias pueden dificultar el análisis del código de evaluación de la misma manera en todos los navegadores.
Cada navegador utiliza un formato de seguimiento de pila diferente para los errores que ocurrieron dentro de eval.
Su código JavaScript también se puede llamar directamente desde el código nativo. Array.prototype.forEach
es un buen ejemplo: pasa una función a forEach
y el motor JS llamará esa función por usted.
función throwErrorWithNativeFrame() { vararr = [0, 1, 2, 3]; arr.forEach(función llamadaFn(valor) {throwError(); });}
Esto produce diferentes seguimientos de pila en diferentes navegadores. Chrome y Safari agregan el nombre de la función nativa en el seguimiento de la pila como un marco separado, como por ejemplo:
(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)
Sin embargo, Firefox e IE11 no muestran que se haya llamado forEach
como parte de la pila:
namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5 throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:3
Algunos navegadores incluyen marcos de código nativo en los seguimientos de la pila, mientras que otros no.
Para detectar que su aplicación tuvo un error, algún código debe poder detectar ese error e informar al respecto. Existen múltiples técnicas para detectar errores, cada una con sus pros y sus contras.
window.onerror
es una de las mejores y más fáciles formas de comenzar a detectar errores. Al asignar window.onerror
a una función, cualquier error que otra parte de la aplicación no detecte se informará a esta función, junto con cierta información sobre el error. Por ejemplo:
ventana.onerror = función (mensaje, URL, línea, columna, err) { console.log('La aplicación encontró un error: ' + msg); console.log('Seguimiento de pila: ' + err.stack);}
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror describe esto con más detalle.
Históricamente, ha habido algunos problemas con este enfoque:
No se proporcionó ningún objeto de error
Se supone que el quinto argumento de la función window.onerror
es un objeto Error. Esto se agregó a la especificación WHATWG en 2013: https://html.spec.whatwg.org/multipage/webappapis.html#errorevent. Chrome, Firefox e IE11 ahora proporcionan correctamente un objeto Error (junto con la propiedad de pila crítica), pero Safari, MS Edge e IE10 no. Esto funciona en Firefox desde Firefox 14 (https://bugzilla.mozilla.org/show_bug.cgi?id=355430) y en Chrome desde finales de 2013 (https://mikewest.org/2013/08/debugging-runtime-errors -con-ventana-en-error, https://code.google.com/p/chromium/issues/detail?id=147127). Safari 10 lanzó soporte para el objeto Error en window.onerror.
Safari (versiones inferiores a 10), MS Edge e IE10 no admiten un objeto Error con un seguimiento de pila en window.onerror.
Desinfección entre dominios
En Chrome, los errores que provienen de otro dominio en el controlador window.onerror se desinfectarán a "Error de secuencia de comandos", "", 0. En general, esto está bien si realmente no desea procesar el error si proviene de un script que no le interesa, por lo que la aplicación puede filtrar errores que se ven como este. Sin embargo, esto no sucede en Firefox, Safari o IE11, ni Chrome lo hace con los bloques try/catch que envuelven el código infractor.
Si desea recibir errores en window.onerror
en Chrome con total fidelidad de scripts entre dominios, esos recursos deben proporcionar los encabezados de origen cruzado adecuados. Consulte https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror para obtener más información.
Chrome es el único navegador que desinfectará los errores que provienen de otro origen. Tenga cuidado de filtrarlos o establecer los encabezados adecuados.
Extensiones de Chrome
En versiones antiguas de Chrome, las extensiones de Chrome que se instalan en la máquina de un usuario también podrían generar errores que se informan en window.onerror. Esto se ha solucionado en las versiones más recientes de Chrome. Consulte la sección dedicada a Extensiones de Chrome a continuación.
La API window.addEventListener("error")
funciona igual que la API window.onerror. Consulte http://www.w3.org/html/wg/drafts/html/master/webappapis.html#runtime-script-errors para obtener más información sobre este enfoque.
Detectar errores a través de window.onerror no evita que ese error también aparezca en la consola de DevTools. Lo más probable es que este sea el comportamiento correcto para el desarrollo, ya que el desarrollador puede ver fácilmente el error. Si no desea que estos errores se muestren en producción a los usuarios finales, se puede llamar e.preventDefault()
si se utiliza el enfoque window.addEventListener.
window.onerror es la mejor herramienta para detectar e informar errores de JS. Se recomienda que solo se informen al servidor los errores de JS con objetos de error y seguimientos de pila válidos; de lo contrario, los errores pueden ser difíciles de investigar o es posible que reciba una gran cantidad de spam de extensiones de Chrome o secuencias de comandos entre dominios.
Dada la sección anterior, lamentablemente no es posible confiar en window.onerror
en todos los navegadores para capturar toda la información de error. Para detectar excepciones localmente, un bloque try/catch es la opción obvia. También es posible envolver archivos JavaScript completos en un bloque try/catch para capturar información de error que no se puede detectar con window.onerror. Esto mejora las situaciones para los navegadores que no soportan window.onerror, pero también tiene algunas desventajas.
Un bloque try/catch no capturará todos los errores de un programa, como los errores que se generan desde un bloque de código asíncrono a través de window.setTimeout
. Try/catch se puede utilizar con puntos de entrada protegidos para ayudar a llenar los vacíos.
Los bloques try/catch que envuelven toda la aplicación no son suficientes para detectar todos los errores.
El compilador no optimizará las funciones de las versiones antiguas de V8 (y potencialmente de otros motores JS) que contienen un bloque try/catch (http://www.html5rocks.com/en/tutorials/speed/v8/). Chrome solucionó este problema en TurboFan (https://codereview.chromium.org/1996373002).
Un "punto de entrada" a JavaScript es cualquier API del navegador que pueda iniciar la ejecución de su código. Los ejemplos incluyen setTimeout
, setInterval
, detectores de eventos, XHR, sockets web o promesas. Los errores que se generan desde estos puntos de entrada serán detectados por window.onerror, pero en los navegadores que no admiten el objeto Error completo en window.onerror, se necesita un mecanismo alternativo para detectar estos errores ya que se menciona el método try/catch. arriba tampoco los atrapará.
Afortunadamente, JavaScript permite ajustar estos puntos de entrada para que se pueda insertar un bloque try/catch antes de invocar la función para detectar cualquier error arrojado por el código.
Cada punto de entrada necesitará un código ligeramente diferente para proteger el punto de entrada, pero la esencia de la metodología es:
función protegerPuntoEntrada(fn) { función de retorno protectedFn() {intenta { return fn();} catch (e) { // Manejar el error.} }}_oldSetTimeout = window.setTimeout;window.setTimeout = función protectedSetTimeout(fn, hora) { return _oldSetTimeout.call(ventana, protectEntryPoint(fn), hora);};
Lamentablemente, es fácil que los errores que ocurren en las Promesas pasen desapercibidos y no se informen. Los errores que ocurren en una Promesa pero que no se manejan adjuntando un controlador de rechazo no se informan en ningún otro lugar; no se informan en window.onerror
. Incluso si una Promesa adjunta un controlador de rechazo, ese código debe informar manualmente esos errores para que se registren. Consulte http://www.html5rocks.com/en/tutorials/es6/promises/#toc-error-handling para obtener más información. Por ejemplo:
ventana.onerror = función(...) { // Esto nunca será invocado por el código de Promesa.};var p = new Promise(...);p.then(function() { throw new Error("Este error no se manejará en ningún lado.");});var p2 = new Promise(...);p2.then(function() { throw new Error("Este error será manejado en la cadena.");}).catch(function(error) { // Mostrar mensaje de error al usuario // Este código debe informar manualmente el error para que se registre en el servidor, si corresponde.});
Un método para capturar más información es utilizar puntos de entrada protegidos para encapsular las invocaciones de métodos de promesa con un try/catch para informar errores. Esto podría verse así:
var _oldPromiseThen = Promesa.prototype.then; Promise.prototype.then = función protectedThen(callback, errorHandler) {return _oldPromiseThen.call(this, protectedEntryPoint(callback), protectedEntryPoint(errorHandler)); };
Lamentablemente, los errores de Promises no se controlarán de forma predeterminada.
Las implementaciones de Promise, como Q, Bluebird y Closure manejan los errores de diferentes maneras, lo que es mejor que el manejo de errores en la implementación de Promises en el navegador.
En Q, puede "finalizar" la cadena de Promesa llamando a .done()
lo que garantizará que si no se manejó un error en la cadena, se volverá a generar y se informará. Ver https://github.com/kriskowal/q#handling-errors
En Bluebird, los rechazos no gestionados se registran y notifican de inmediato. Consulte http://bluebirdjs.com/docs/features.html#surfacing-unhandled-errors
En la implementación de goog.Promise de Clausura, los rechazos no controlados se registran y reportan si ninguna cadena en la Promesa maneja el rechazo dentro de un intervalo de tiempo configurable (para permitir que el código más adelante en el programa agregue un controlador de rechazo).
La sección de seguimiento de pila asíncrona anterior analiza que los navegadores no capturan información de la pila cuando hay un enlace asíncrono, como llamar Promise.prototype.then
. Los polyfills de promesa presentan una forma de capturar los puntos de seguimiento de la pila asíncrona, lo que puede facilitar mucho el diagnóstico de errores. Este enfoque es costoso, pero puede resultar realmente útil para capturar más información de depuración.
En Q, llame Q.longStackSupport = true;
. Ver https://github.com/kriskowal/q#long-stack-traces
En Bluebird, llame Promise.longStackTraces()
en algún lugar de la aplicación. Consulte http://bluebirdjs.com/docs/features.html#long-stack-traces.
En Cierre, establezca goog.Promise.LONG_STACK_TRACES
en verdadero.
Chrome 49 agregó soporte para eventos que se envían cuando se rechaza una Promesa. Esto permite que las aplicaciones se conecten a los errores de Promise para garantizar que se informen de forma centralizada junto con el resto de los errores.
window.addEventListener('unhandledrejection', evento => { // event.reason contiene el motivo del rechazo. Cuando se produce un error, este es el objeto Error.});
Consulte https://googlechrome.github.io/samples/promise-rejection-events/ y https://www.chromestatus.com/feature/4805872211460096 para obtener más información.
Esto no es compatible con ningún otro navegador.
Los trabajadores web, incluidos los trabajadores dedicados, los trabajadores compartidos y los trabajadores de servicios, se están volviendo más populares en las aplicaciones actuales. Dado que todos estos trabajadores son scripts separados de la página principal, cada uno necesita su propio código de manejo de errores. Se recomienda que cada script de trabajador instale su propio código de informe y manejo de errores para lograr la máxima eficacia en el manejo de errores de los trabajadores.
Los trabajadores web dedicados se ejecutan en un contexto de ejecución diferente al de la página principal, por lo que los mecanismos anteriores no detectan los errores de los trabajadores. Es necesario tomar medidas adicionales para capturar errores de los trabajadores en la página.
Cuando se crea un trabajador, la propiedad onerror se puede establecer en el nuevo trabajador:
var trabajador = nuevo Trabajador('worker.js');trabajador.onerror = función(errorEvent) {...};
Esto se define en https://html.spec.whatwg.org/multipage/workers.html#handler-abstractworker-onerror. La función onerror
del trabajador tiene una firma diferente a la de window.onerror
discutida anteriormente. En lugar de aceptar 5 argumentos, worker.onerror
toma un único argumento: un objeto ErrorEvent
. La API para este objeto se puede encontrar en https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent. Contiene el mensaje, el nombre del archivo, la línea y la columna, pero ningún navegador estable actualmente contiene el objeto "Error" que contiene el seguimiento de la pila (errorEvent.error es nulo). Dado que esta API se ejecuta en el ámbito de la página principal, sería útil utilizar el mismo mecanismo de informes que la página principal; Desafortunadamente, debido a la falta de seguimiento de la pila, esta API tiene un uso limitado.
Dentro del JS ejecutado por el trabajador, también puede definir una API onerror que siga la API window.onerror habitual: https://html.spec.whatwg.org/multipage/webappapis.html#onerroreventhandler. En el código de trabajador:
self.onerror = función (mensaje, nombre de archivo, línea, columna, error) {...};
La discusión sobre esta API sigue principalmente la discusión anterior sobre window.onerror. Sin embargo, hay 2 cosas notables a señalar:
Firefox y Safari no informan el objeto "error" como quinto argumento de la función, por lo que estos navegadores no obtienen un seguimiento de la pila del trabajador (Chrome, MS Edge e IE11 sí obtienen un seguimiento de la pila). Los puntos de entrada protegidos para la función onmessage
dentro del trabajador se pueden usar para capturar información de seguimiento de la pila para estos navegadores.
Dado que este código se ejecuta dentro del trabajador, el código debe elegir cómo informar el error al servidor: debe usar postMessage
para comunicar el error a la página principal o instalar un mecanismo de informe de errores XHR (que se analiza más adelante) en el propio trabajador.
En Firefox, Safari e IE11 (pero no en Chrome), la función window.onerror
de la página principal también se llamará después de que se haya llamado al onerror del trabajador y al detector de eventos onerror establecido por la página. Sin embargo, este window.onerror tampoco contendrá un objeto de error y, por lo tanto, tampoco tendrá un seguimiento de la pila. Estos navegadores también deben tener cuidado de no informar errores de los trabajadores varias veces.
Chrome y Firefox admiten la API SharedWorker para compartir un trabajador entre varias páginas. Dado que el trabajador es compartido, no está adjunto exclusivamente a una página principal; Esto genera algunas diferencias en cómo se manejan los errores, aunque SharedWorker sigue principalmente la misma información que el trabajador web dedicado.
En Chrome, cuando hay un error en un SharedWorker, solo se llamará el manejo de errores del propio trabajador dentro del código del trabajador (como si configuraran self.onerror
). No se llamará al window.onerror
de la página principal y Chrome no admite el AbstractWorker.onerror
heredado que se puede llamar en la página principal como se define en la especificación.
En Firefox, este comportamiento es diferente. Un error en el trabajador compartido hará que se llame a window.onerror de la página principal, pero el objeto de error será nulo. Además, Firefox admite la propiedad AbstractWorker.onerror
, por lo que la página principal puede adjuntar su propio controlador de errores al trabajador. Sin embargo, cuando se llama a este controlador de errores, el objeto de error será nulo, por lo que no habrá seguimiento de la pila, por lo que es de uso limitado.
El manejo de errores para trabajadores compartidos difiere según el navegador.
Service Workers es una especificación completamente nueva que actualmente solo está disponible en las versiones recientes de Chrome y Firefox. Estos trabajadores siguen la misma discusión que los trabajadores web dedicados.
Los trabajadores del servicio se instalan llamando a la función navigator.serviceWorker.register
. Esta función devuelve una Promesa que será rechazada si hubo un error al instalar el trabajador del servicio, como por ejemplo si arroja un error durante la inicialización. Este error sólo contendrá un mensaje de cadena y nada más. Además, dado que las Promesas no informan errores a los controladores window.onerror
, la aplicación misma tendría que agregar un bloque catch a la Promesa para detectar el error.
navigator.serviceWorker.register('service-worker-installation-error.js').catch(function(error) { // tipo de error de cadena});
Al igual que los demás trabajadores, los trabajadores de servicios pueden configurar una función self.onerror
dentro de los trabajadores de servicios para detectar errores. Los errores de instalación en el trabajador del servicio se informarán a la función onerror, pero desafortunadamente no contendrán un objeto de error ni un seguimiento de la pila.
La API del trabajador del servicio contiene una propiedad onerror heredada de la interfaz AbstractWorker, pero Chrome no hace nada.