Una guía definitiva para manejar errores en JavaScript

Publicado: 2022-01-24

La ley de Murphy establece que cualquier cosa que pueda salir mal eventualmente saldrá mal. Esto se aplica demasiado bien en el mundo de la programación. Si crea una aplicación, es probable que cree errores y otros problemas. ¡Los errores en JavaScript son uno de esos problemas comunes!

El éxito de un producto de software depende de qué tan bien sus creadores puedan resolver estos problemas antes de perjudicar a sus usuarios. Y JavaScript, de todos los lenguajes de programación, es conocido por su diseño promedio de manejo de errores.

Si está creando una aplicación de JavaScript, existe una alta probabilidad de que se equivoque con los tipos de datos en un momento u otro. Si no es así, entonces podría terminar reemplazando un indefinido con un operador nulo o triple igual ( === ) con un operador doble igual ( == ).

Es humano cometer errores. Es por eso que le mostraremos todo lo que necesita saber sobre el manejo de errores en JavaScript.

Este artículo lo guiará a través de los errores básicos en JavaScript y explicará los diversos errores que puede encontrar. A continuación, aprenderá a identificar y corregir estos errores. También hay un par de consejos para manejar los errores de manera efectiva en entornos de producción.

Sin más preámbulos, ¡comencemos!

¿Qué son los errores de JavaScript?

Los errores de programación se refieren a situaciones que no permiten que un programa funcione normalmente. Puede suceder cuando un programa no sabe cómo manejar el trabajo en cuestión, como cuando intenta abrir un archivo inexistente o se comunica con un punto final de API basado en la web mientras no hay conectividad de red.

Estas situaciones empujan al programa a arrojar errores al usuario, indicando que no sabe cómo proceder. El programa recopila la mayor cantidad de información posible sobre el error y luego informa que no puede avanzar.

La ley de Murphy establece que cualquier cosa que pueda salir mal eventualmente saldrá mal Esto se aplica demasiado bien en el mundo de JavaScript Prepárate con esta guía Haz clic para twittear

Los programadores inteligentes intentan predecir y cubrir estos escenarios para que el usuario no tenga que descifrar un mensaje de error técnico como "404" de forma independiente. En cambio, muestran un mensaje mucho más comprensible: “No se pudo encontrar la página”.

Los errores en JavaScript son objetos que se muestran cada vez que ocurre un error de programación. Estos objetos contienen amplia información sobre el tipo de error, la declaración que causó el error y el seguimiento de la pila cuando ocurrió el error. JavaScript también permite a los programadores crear errores personalizados para proporcionar información adicional al depurar problemas.

Propiedades de un error

Ahora que la definición de un error de JavaScript está clara, es hora de profundizar en los detalles.

Los errores en JavaScript tienen ciertas propiedades estándar y personalizadas que ayudan a comprender la causa y los efectos del error. Por defecto, los errores en JavaScript contienen tres propiedades:

  1. mensaje : un valor de cadena que lleva el mensaje de error
  2. nombre : el tipo de error que ocurrió (profundizaremos en esto en la siguiente sección)
  3. stack : el seguimiento de la pila del código ejecutado cuando ocurrió el error.

Además, los errores también pueden tener propiedades como columnNumber, lineNumber, fileName, etc., para describir mejor el error. Sin embargo, estas propiedades no son estándar y pueden o no estar presentes en todos los objetos de error generados desde su aplicación de JavaScript.

Comprender el seguimiento de la pila

Un seguimiento de pila es la lista de llamadas a métodos en las que se encontraba un programa cuando ocurre un evento como una excepción o una advertencia. Así es como se ve un rastro de pila de muestra acompañado de una excepción:

El error "Error de tipo: se espera un argumento numérico" se muestra sobre un fondo gris con detalles adicionales de la pila.
Ejemplo de un seguimiento de pila.

Como puede ver, comienza imprimiendo el nombre y el mensaje del error, seguido de una lista de métodos que se estaban llamando. Cada llamada de método indica la ubicación de su código fuente y la línea en la que se invocó. Puede usar estos datos para navegar a través de su base de código e identificar qué fragmento de código está causando el error.

Esta lista de métodos está organizada de forma apilada. Muestra dónde se lanzó por primera vez su excepción y cómo se propagó a través de las llamadas a métodos apilados. La implementación de una captura para la excepción no permitirá que se propague a través de la pila y bloquee su programa. Sin embargo, es posible que desee dejar los errores fatales sin detectar para bloquear el programa en algunos escenarios intencionalmente.

Errores frente a excepciones

La mayoría de la gente suele considerar los errores y las excepciones como la misma cosa. Sin embargo, es esencial tener en cuenta una diferencia leve pero fundamental entre ellos.

Para entender esto mejor, tomemos un ejemplo rápido. Así es como puede definir un error en JavaScript:

 const wrongTypeError = TypeError("Wrong type found, expected character")

Y así es como el objeto wrongTypeError se convierte en una excepción:

 throw wrongTypeError

Sin embargo, la mayoría de la gente tiende a usar la forma abreviada que define los objetos de error mientras los lanza:

 throw TypeError("Wrong type found, expected character")

Esta es una práctica estándar. Sin embargo, es una de las razones por las que los desarrolladores tienden a mezclar excepciones y errores. Por lo tanto, conocer los fundamentos es vital, aunque use taquigrafía para hacer su trabajo rápidamente.

Tipos de errores en JavaScript

Hay una variedad de tipos de errores predefinidos en JavaScript. Son elegidos y definidos automáticamente por el tiempo de ejecución de JavaScript siempre que el programador no maneje explícitamente los errores en la aplicación.

Esta sección lo guiará a través de algunos de los tipos de errores más comunes en JavaScript y comprenderá cuándo y por qué ocurren.

RangeError

Se lanza un RangeError cuando una variable se establece con un valor fuera de su rango de valores legales. Por lo general, ocurre cuando se pasa un valor como argumento a una función y el valor dado no se encuentra en el rango de los parámetros de la función. A veces puede resultar complicado solucionarlo cuando se utilizan bibliotecas de terceros mal documentadas, ya que necesita conocer el rango de valores posibles para que los argumentos pasen el valor correcto.

Algunos de los escenarios comunes en los que se produce RangeError son:

  • Intentando crear una matriz de longitudes ilegales a través del constructor Array.
  • Pasar valores incorrectos a métodos numéricos como toExponential() , toPrecision() , toFixed() , etc.
  • Pasar valores ilegales a funciones de cadena como normalize() .

Error de referencia

Un ReferenceError ocurre cuando algo está mal con la referencia de una variable en su código. Es posible que haya olvidado definir un valor para la variable antes de usarla o que esté tratando de usar una variable inaccesible en su código. En cualquier caso, pasar por el seguimiento de la pila proporciona amplia información para encontrar y corregir la referencia de la variable que tiene la culpa.

Algunas de las razones comunes por las que se producen los errores de referencia son:

  • Hacer un error tipográfico en un nombre de variable.
  • Intentar acceder a variables de ámbito de bloque fuera de sus ámbitos.
  • Hacer referencia a una variable global de una biblioteca externa (como $ de jQuery) antes de que se cargue.

Error de sintaxis

Estos errores son de los más sencillos de solucionar ya que indican un error en la sintaxis del código. Dado que JavaScript es un lenguaje de secuencias de comandos que se interpreta en lugar de compilar, estos se lanzan cuando la aplicación ejecuta la secuencia de comandos que contiene el error. En el caso de lenguajes compilados, dichos errores se identifican durante la compilación. Por lo tanto, los archivos binarios de la aplicación no se crean hasta que se corrigen.

Algunas de las razones comunes por las que pueden ocurrir SyntaxErrors son:

  • Faltan comillas
  • Faltan paréntesis de cierre
  • Alineación incorrecta de llaves u otros caracteres

Es una buena práctica usar una herramienta de pelusa en su IDE para identificar dichos errores antes de que lleguen al navegador.

Error de tecleado

TypeError es uno de los errores más comunes en las aplicaciones de JavaScript. Este error se crea cuando algún valor no resulta ser de un tipo esperado en particular. Algunos de los casos comunes cuando ocurre son:

  • Invocar objetos que no son métodos.
  • Intentar acceder a las propiedades de objetos nulos o indefinidos
  • Tratar una cadena como un número o viceversa

Hay muchas más posibilidades en las que puede ocurrir un TypeError. Veremos algunos casos famosos más adelante y aprenderemos a solucionarlos.

Error interno

El tipo InternalError se utiliza cuando se produce una excepción en el motor de tiempo de ejecución de JavaScript. Puede o no indicar un problema con su código.

La mayoría de las veces, InternalError ocurre solo en dos escenarios:

  • Cuando un parche o una actualización del tiempo de ejecución de JavaScript tiene un error que arroja excepciones (esto sucede muy raramente)
  • Cuando su código contiene entidades que son demasiado grandes para el motor de JavaScript (por ejemplo, demasiados casos de cambio, inicializadores de matriz demasiado grandes, demasiada recursividad)

El enfoque más apropiado para resolver este error es identificar la causa a través del mensaje de error y reestructurar la lógica de la aplicación, si es posible, para eliminar el aumento repentino de la carga de trabajo en el motor de JavaScript.

URIError

URIError ocurre cuando una función de manejo de URI global como decodeURIComponent se usa ilegalmente. Por lo general, indica que el parámetro pasado a la llamada al método no se ajustaba a los estándares de URI y, por lo tanto, el método no lo analizó correctamente.

El diagnóstico de estos errores suele ser fácil, ya que solo necesita examinar los argumentos de malformación.

EvalError

Un EvalError ocurre cuando ocurre un error con una llamada a la función eval() . La función eval() se usa para ejecutar código JavaScript almacenado en cadenas. Sin embargo, dado que se desaconseja encarecidamente el uso de la función eval() debido a problemas de seguridad y las especificaciones ECMAScript actuales ya no arrojan la clase EvalError , este tipo de error existe simplemente para mantener la compatibilidad con versiones anteriores del código JavaScript heredado.

Si está trabajando en una versión anterior de JavaScript, es posible que encuentre este error. En cualquier caso, es mejor investigar el código ejecutado en la llamada a la función eval() en busca de excepciones.

Creación de tipos de error personalizados

Si bien JavaScript ofrece una lista adecuada de clases de tipos de error para cubrir la mayoría de los escenarios, siempre puede crear un nuevo tipo de error si la lista no satisface sus requisitos. La base de esta flexibilidad radica en el hecho de que JavaScript te permite lanzar cualquier cosa literalmente con el comando throw .

Entonces, técnicamente, estas declaraciones son completamente legales:

 throw 8 throw "An error occurred"

Sin embargo, arrojar un tipo de datos primitivo no proporciona detalles sobre el error, como su tipo, nombre o el seguimiento de la pila que lo acompaña. Para corregir esto y estandarizar el proceso de manejo de errores, se ha proporcionado la clase Error . También se desaconseja usar tipos de datos primitivos al generar excepciones.

Puede ampliar la clase de Error para crear su clase de error personalizada. Aquí hay un ejemplo básico de cómo puedes hacer esto:

 class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; } }

Y puedes usarlo de la siguiente manera:

 throw ValidationError("Property not found: name")

Y luego puede identificarlo usando la palabra clave instanceof :

 try { validateForm() // code that throws a ValidationError } catch (e) { if (e instanceof ValidationError) // do something else // do something else }

Los 10 errores más comunes en JavaScript

Ahora que comprende los tipos de errores comunes y cómo crear los personalizados, es hora de ver algunos de los errores más comunes que enfrentará al escribir código JavaScript.

1. RangeError no detectado

Este error ocurre en Google Chrome en algunos escenarios diferentes. Primero, puede suceder si llama a una función recursiva y no termina. Puedes comprobarlo tú mismo en Chrome Developer Console:

El error "Error de rango no detectado: se excedió el tamaño máximo de la pila de llamadas" se muestra sobre un fondo rojo junto a un icono de cruz roja con el código de una función recursiva encima.
Ejemplo de RangeError con una llamada de función recursiva.

Entonces, para resolver tal error, asegúrese de definir correctamente los casos límite de su función recursiva. Otra razón por la que ocurre este error es si ha pasado un valor que está fuera del rango del parámetro de una función. Aquí hay un ejemplo:

El error "Error de rango no detectado: el argumento toExponential() debe estar entre 0 y 100" se muestra sobre un fondo rojo junto a un icono de cruz roja con una llamada a la función toExponential() encima.
Ejemplo de RangeError con la llamada toExponential().

El mensaje de error generalmente indicará qué está mal con su código. Una vez que realice los cambios, se resolverá.

num = 4. num.toExponential(2). Salida: 4.00e+0.
Salida para la llamada a la función toExponential().

2. TypeError no detectado: no se puede establecer la propiedad

Este error ocurre cuando establece una propiedad en una referencia indefinida. Puede reproducir el problema con este código:

 var list list.count = 0

Este es el resultado que recibirá:

El error "Error de tipo no detectado: no se pueden establecer propiedades de indefinido" se muestra sobre un fondo rojo junto a un icono de cruz roja con una asignación list.count = 0 encima.
Ejemplo de TypeError.

Para corregir este error, inicialice la referencia con un valor antes de acceder a sus propiedades. Así es como se ve cuando se arregla:

Establecer list.count = 10 después de inicializar la lista con {} debido a que la salida es 10.
Cómo arreglar TypeError.

3. TypeError no capturado: no se puede leer la propiedad

Este es uno de los errores más frecuentes en JavaScript. Este error ocurre cuando intenta leer una propiedad o llamar a una función en un objeto indefinido. Puede reproducirlo muy fácilmente ejecutando el siguiente código en una consola de Chrome Developer:

 var func func.call()

Aquí está la salida:

El error "TypeError no detectado: no se pueden leer las propiedades de undefined" se muestra sobre un fondo rojo junto a un icono de cruz roja con func.call() encima.
Ejemplo de TypeError con función indefinida.

Un objeto indefinido es una de las muchas posibles causas de este error. Otra causa importante de este problema puede ser una inicialización incorrecta del estado al representar la interfaz de usuario. Aquí hay un ejemplo del mundo real de una aplicación React:

 import React, { useState, useEffect } from "react"; const CardsList = () => { const [state, setState] = useState(); useEffect(() => { setTimeout(() => setState({ items: ["Card 1", "Card 2"] }), 2000); }, []); return ( <> {state.items.map((item) => ( <li key={item}>{item}</li> ))} </> ); }; export default CardsList;

La aplicación comienza con un contenedor de estado vacío y recibe algunos elementos después de un retraso de 2 segundos. El retardo se establece para imitar una llamada de red. Incluso si su red es súper rápida, aún enfrentará una pequeña demora debido a que el componente se procesará al menos una vez. Si intenta ejecutar esta aplicación, recibirá el siguiente error:

El error "indefinido no es un objeto" se muestra sobre un fondo gris.
Rastreo de pila TypeError en un navegador.

Esto se debe a que, en el momento de la representación, el contenedor de estado no está definido; por lo tanto, no existen items de propiedad en él. Reparar este error es fácil. Solo necesita proporcionar un valor predeterminado inicial al contenedor de estado.

 // ... const [state, setState] = useState({items: []}); // ...

Ahora, después del retraso establecido, su aplicación mostrará un resultado similar:

Una lista con viñetas con dos elementos que dicen "Tarjeta 1" y "Tarjeta 2".
Salida de código.

La solución exacta en su código puede ser diferente, pero la esencia aquí es siempre inicializar sus variables correctamente antes de usarlas.

4. TypeError: 'indefinido' no es un objeto

Este error ocurre en Safari cuando intenta acceder a las propiedades o llamar a un método en un objeto no definido. Puede ejecutar el mismo código anterior para reproducir el error usted mismo.

El error "TypeError: indefinido no es un objeto" que se muestra sobre un fondo rojo junto a un icono de signo de exclamación rojo con la llamada al método func.call() encima.
Ejemplo de TypeError con función indefinida.

La solución a este error también es la misma: asegúrese de haber inicializado sus variables correctamente y que no estén indefinidas cuando se accede a una propiedad o método.

5. TypeError: nulo no es un objeto

Esto es, de nuevo, similar al error anterior. Ocurre en Safari, y la única diferencia entre los dos errores es que este se produce cuando el objeto cuya propiedad o método se accede es null en lugar de undefined . Puede reproducir esto ejecutando el siguiente fragmento de código:

 var func = null func.call()

Este es el resultado que recibirá:

El mensaje de error "TypeError: null is not an object", que se muestra sobre un fondo rojo junto a un icono de signo de exclamación rojo.
Ejemplo de TypeError con función nula.

Dado que null es un valor establecido explícitamente en una variable y JavaScript no lo asigna automáticamente. Este error puede ocurrir solo si está intentando acceder a una variable que usted mismo estableció como null . Por lo tanto, debe revisar su código y verificar si la lógica que escribió es correcta o no.

6. TypeError: no se puede leer la propiedad 'longitud'

Este error ocurre en Chrome cuando intentas leer la longitud de un objeto null o undefined . La causa de este problema es similar a los problemas anteriores, pero ocurre con bastante frecuencia al manejar listas; por lo que merece una mención especial. Así es como puede reproducir el problema:

El error "Error de tipo no capturado: no se puede leer la propiedad 'longitud' de indefinido" que se muestra en un fondo rojo junto a un icono de cruz roja con la llamada myButton.length encima.
Ejemplo de TypeError con un objeto indefinido.

Sin embargo, en las versiones más recientes de Chrome, este error se informa como Uncaught TypeError: Cannot read properties of undefined . Asi es como luce ahora:

El error "Error de tipo no capturado: no se pueden leer las propiedades de undefined" que se muestra en un fondo rojo junto a un icono de cruz roja con la llamada myButton.length encima.
Ejemplo de TypeError con un objeto indefinido en las versiones más recientes de Chrome.

La solución, nuevamente, es asegurarse de que el objeto a cuya longitud está tratando de acceder exista y no esté configurado como null .

7. TypeError: 'indefinido' no es una función

Este error ocurre cuando intenta invocar un método que no existe en su secuencia de comandos, o existe, pero no se puede hacer referencia en el contexto de la llamada. Este error suele ocurrir en Google Chrome, y puedes solucionarlo comprobando la línea de código que arroja el error. Si encuentra un error tipográfico, arréglelo y verifique si resuelve su problema.

Si ha utilizado la palabra clave de autorreferencia this en su código, este error podría surgir si this está debidamente vinculado a su contexto. Considere el siguiente código:

 function showAlert() { alert("message here") } document.addEventListener("click", () => { this.showAlert(); })

Si ejecuta el código anterior, arrojará el error que discutimos. Ocurre porque la función anónima pasada como detector de eventos se está ejecutando en el contexto del document .

Por el contrario, la función showAlert se define en el contexto de la window .

Para resolver esto, debe pasar la referencia adecuada a la función vinculándola con el método bind() :

 document.addEventListener("click", this.showAlert.bind(this))

8. ReferenceError: el evento no está definido

Este error ocurre cuando intenta acceder a una referencia no definida en el alcance de la llamada. Esto suele suceder cuando se manejan eventos, ya que a menudo le brindan una referencia llamada event en la función de devolución de llamada. Este error puede ocurrir si olvida definir el argumento del evento en los parámetros de su función o lo escribe mal.

Es posible que este error no ocurra en Internet Explorer o Google Chrome (ya que IE ofrece una variable de evento global y Chrome adjunta la variable de evento automáticamente al controlador), pero puede ocurrir en Firefox. Por lo tanto, es recomendable estar atento a errores tan pequeños.

9. TypeError: Asignación a variable constante

Este es un error que surge por descuido. Si intenta asignar un nuevo valor a una variable constante, se encontrará con este resultado:

El error "Error de tipo no detectado: Asignación a variable constante" que se muestra en un fondo rojo junto a un icono de cruz roja con función = 6 asignación encima.
Ejemplo de TypeError con asignación de objeto constante.

Si bien parece fácil de arreglar en este momento, imagine cientos de declaraciones de variables de este tipo y una de ellas definida erróneamente como const en lugar de let . A diferencia de otros lenguajes de secuencias de comandos como PHP, existe una diferencia mínima entre el estilo de declaración de constantes y variables en JavaScript. Por lo tanto, es recomendable verificar sus declaraciones en primer lugar cuando se encuentre con este error. También podría encontrarse con este error si olvida que dicha referencia es una constante y la usa como una variable. Esto indica descuido o una falla en la lógica de su aplicación. Asegúrese de marcar esto cuando intente solucionar este problema.

10. (desconocido): error de secuencia de comandos

Un error de secuencia de comandos ocurre cuando una secuencia de comandos de terceros envía un error a su navegador. Este error va seguido de (desconocido) porque la secuencia de comandos de terceros pertenece a un dominio diferente al de su aplicación. El navegador oculta otros detalles para evitar la filtración de información confidencial del script de terceros.

No puede resolver este error sin conocer los detalles completos. Esto es lo que puede hacer para obtener más información sobre el error:

  1. Agregue el atributo crossorigin en la etiqueta del script.
  2. Establezca el encabezado Access-Control-Allow-Origin correcto en el servidor que aloja el script.
  3. [Opcional] Si no tiene acceso al servidor que aloja la secuencia de comandos, puede considerar usar un proxy para transmitir su solicitud al servidor y al cliente con los encabezados correctos.

Una vez que pueda acceder a los detalles del error, puede establecerse para solucionar el problema, que probablemente estará en la biblioteca de terceros o en la red.

Cómo identificar y prevenir errores en JavaScript

Si bien los errores discutidos anteriormente son los más comunes y frecuentes en JavaScript, se encontrará con que confiar en algunos ejemplos nunca puede ser suficiente. Es vital comprender cómo detectar y prevenir cualquier tipo de error en una aplicación de JavaScript mientras se desarrolla. Así es como puede manejar los errores en JavaScript.

Lanzar y atrapar errores manualmente

La forma más fundamental de manejar los errores que se han producido manualmente o por el tiempo de ejecución es detectarlos. Como la mayoría de los otros lenguajes, JavaScript ofrece un conjunto de palabras clave para manejar errores. Es esencial conocer cada uno de ellos en profundidad antes de comenzar a manejar los errores en su aplicación de JavaScript.

tirar

La primera y más básica palabra clave del conjunto es throw . Como es evidente, la palabra clave throw se utiliza para generar errores para crear excepciones en el tiempo de ejecución de JavaScript de forma manual. Ya hemos discutido esto anteriormente en el artículo, y aquí está la esencia del significado de esta palabra clave:

  • Puede throw cualquier cosa, incluidos números, cadenas y objetos de Error .
  • Sin embargo, no es recomendable arrojar tipos de datos primitivos, como cadenas y números, ya que no contienen información de depuración sobre los errores.
  • Ejemplo: throw TypeError("Please provide a string")

tratar

La palabra clave try se usa para indicar que un bloque de código podría generar una excepción. Su sintaxis es:

 try { // error-prone code here }

Es importante tener en cuenta que un bloque catch siempre debe seguir al bloque try para manejar los errores de manera efectiva.

captura

La palabra clave catch se utiliza para crear un bloque catch. Este bloque de código es responsable de manejar los errores que detecta el bloque de try final. Aquí está su sintaxis:

 catch (exception) { // code to handle the exception here }

Y así es como implementas los bloques try y catch juntos:

 try { // business logic code } catch (exception) { // error handling code }

A diferencia de C++ o Java, no puede agregar varios bloques catch a un bloque try en JavaScript. Esto significa que no puedes hacer esto:

 try { // business logic code } catch (exception) { if (exception instanceof TypeError) { // do something } } catch (exception) { if (exception instanceof RangeError) { // do something } }

En su lugar, puede usar una instrucción if...else o una instrucción switch case dentro del bloque catch único para manejar todos los casos de error posibles. Se vería así:

 try { // business logic code } catch (exception) { if (exception instanceof TypeError) { // do something } else if (exception instanceof RangeError) { // do something else } }

por fin

La palabra clave finally se usa para definir un bloque de código que se ejecuta después de que se ha manejado un error. Este bloque se ejecuta después de los bloques try y catch.

Además, el bloque finalmente se ejecutará independientemente del resultado de los otros dos bloques. Esto significa que incluso si el bloque catch no puede manejar el error por completo o si se arroja un error en el bloque catch, el intérprete ejecutará el código en el último bloque antes de que el programa se bloquee.

Para que se considere válido, el bloque de prueba en JavaScript debe ir seguido de un bloque de captura o finalmente. Sin ninguno de ellos, el intérprete generará un SyntaxError. Por lo tanto, asegúrese de seguir sus bloques de prueba con al menos cualquiera de ellos cuando maneje errores.

Manejar errores globalmente con el método onerror()

El método onerror() está disponible para todos los elementos HTML para manejar cualquier error que pueda ocurrir con ellos. Por ejemplo, si una etiqueta img no puede encontrar la imagen cuya URL se especifica, activa su método onerror para permitir que el usuario maneje el error.

Por lo general, proporcionaría otra URL de imagen en la llamada onerror para que la etiqueta img recurra. Así es como puedes hacerlo a través de JavaScript:

 const image = document.querySelector("img") image.onerror = (event) => { console.log("Error occurred: " + event) }

Sin embargo, puede usar esta característica para crear un mecanismo global de manejo de errores para su aplicación. Así es como puedes hacerlo:

 window.onerror = (event) => { console.log("Error occurred: " + event) }

Con este controlador de eventos, puede deshacerse de los múltiples bloques try...catch que se encuentran en su código y centralizar el manejo de errores de su aplicación de manera similar al manejo de eventos. Puede adjuntar varios controladores de errores a la ventana para mantener el principio de responsabilidad única de los principios de diseño de SOLID. El intérprete recorrerá todos los controladores hasta que llegue al adecuado.

Pasar errores a través de devoluciones de llamada

Si bien las funciones simples y lineales permiten que el manejo de errores siga siendo simple, las devoluciones de llamadas pueden complicar el asunto.

Considere la siguiente pieza de código:

¿Necesita una solución de hospedaje que le brinde una ventaja competitiva? Kinsta lo tiene cubierto con una velocidad increíble, seguridad de última generación y escalado automático. Consulta nuestros planes

 const calculateCube = (number, callback) => { setTimeout(() => { const cube = number * number * number callback(cube) }, 1000) } const callback = result => console.log(result) calculateCube(4, callback)

La función anterior demuestra una condición asincrónica en la que una función tarda un tiempo en procesar las operaciones y devuelve el resultado más tarde con la ayuda de una devolución de llamada.

Si intenta ingresar una cadena en lugar de 4 en la llamada de función, obtendrá NaN como resultado.

Esto debe manejarse adecuadamente. Así es cómo:

 const calculateCube = (number, callback) => { setTimeout(() => { if (typeof number !== "number") throw new Error("Numeric argument is expected") const cube = number * number * number callback(cube) }, 1000) } const callback = result => console.log(result) try { calculateCube(4, callback) } catch (e) { console.log(e) }

Esto debería resolver el problema idealmente. Sin embargo, si intenta pasar una cadena a la llamada de función, recibirá esto:

El error "Error no detectado: se espera un argumento numérico" que se muestra sobre un fondo rojo oscuro junto a un icono de cruz roja.
Ejemplo de error con el argumento incorrecto.

Aunque haya implementado un bloque try-catch al llamar a la función, aún dice que el error no se detectó. El error se produce después de que se haya ejecutado el bloque catch debido al retraso del tiempo de espera.

Esto puede ocurrir rápidamente en las llamadas de la red, donde se producen retrasos inesperados. Debe cubrir estos casos mientras desarrolla su aplicación.

Así es como puede manejar los errores correctamente en las devoluciones de llamada:

 const calculateCube = (number, callback) => { setTimeout(() => { if (typeof number !== "number") { callback(new TypeError("Numeric argument is expected")) return } const cube = number * number * number callback(null, cube) }, 2000) } const callback = (error, result) => { if (error !== null) { console.log(error) return } console.log(result) } try { calculateCube('hey', callback) } catch (e) { console.log(e) }

Ahora, la salida en la consola será:

El mensaje "Error de tipo: se espera un argumento numérico" que se muestra sobre un fondo gris oscuro.
Ejemplo de TypeError con argumento ilegal.

Esto indica que el error se ha manejado adecuadamente.

Manejar errores en promesas

La mayoría de las personas tienden a preferir las promesas para manejar actividades asincrónicas. Las promesas tienen otra ventaja: una promesa rechazada no finaliza su script. Sin embargo, aún necesita implementar un bloque catch para manejar los errores en las promesas. Para entender esto mejor, reescribamos la función de calculateCube() usando Promesas:

 const delay = ms => new Promise(res => setTimeout(res, ms)); const calculateCube = async (number) => { if (typeof number !== "number") throw Error("Numeric argument is expected") await delay(5000) const cube = number * number * number return cube } try { calculateCube(4).then(r => console.log(r)) } catch (e) { console.log(e) }

El tiempo de espera del código anterior se ha aislado en la función de delay para su comprensión. Si intenta ingresar una cadena en lugar de 4, el resultado que obtendrá será similar a este:

El error "Error no capturado (en promesa): se espera un argumento numérico" que se muestra en un fondo gris oscuro junto a un icono de cruz roja.
Ejemplo de TypeError con un argumento ilegal en Promise.

Nuevamente, esto se debe a que Promise arroja el error después de que todo lo demás haya completado la ejecución. La solución a este problema es simple. Simplemente agregue una llamada catch() a la cadena de promesas como esta:

 calculateCube("hey") .then(r => console.log(r)) .catch(e => console.log(e))

Ahora la salida será:

El mensaje "Error: se esperaba un argumento numérico" se muestra sobre un fondo gris oscuro.
Ejemplo de TypeError manejado con argumento ilegal.

Puede observar lo fácil que es manejar errores con promesas. Además, puede encadenar un bloque finally() y la llamada de promesa para agregar código que se ejecutará después de que se haya completado el manejo de errores.

Alternativamente, también puede manejar los errores en las promesas usando la técnica tradicional de intentar-atrapar-finalmente. Así es como se vería tu llamada de promesa en ese caso:

 try { let result = await calculateCube("hey") console.log(result) } catch (e) { console.log(e) } finally { console.log('Finally executed") }

Sin embargo, esto funciona solo dentro de una función asíncrona. Por lo tanto, la forma preferida de manejar los errores en las promesas es encadenar la catch y, finally , la llamada de la promesa.

throw/catch vs onerror() vs Callbacks vs Promises: ¿Cuál es el mejor?

Con cuatro métodos a su disposición, debe saber elegir el más adecuado en cualquier caso de uso. Así es como pueden decidir por sí mismos:

lanzar/atrapar

Usará este método la mayor parte del tiempo. Asegúrese de implementar condiciones para todos los errores posibles dentro de su bloque catch y recuerde incluir un bloque finalmente si necesita ejecutar algunas rutinas de limpieza de memoria después del bloque try.

Sin embargo, demasiados bloques try/catch pueden hacer que su código sea difícil de mantener. Si se encuentra en tal situación, es posible que desee manejar los errores a través del controlador global o el método de promesa.

Al decidir entre los bloques try/catch asincrónicos y catch() de la promesa, es recomendable optar por los bloques try/catch asíncronos, ya que harán que su código sea lineal y fácil de depurar.

onerror()

Es mejor usar el método onerror() cuando sabe que su aplicación tiene que manejar muchos errores y pueden estar bien dispersos en la base de código. El método onerror le permite manejar los errores como si fueran un evento más manejado por su aplicación. Puede definir varios controladores de errores y adjuntarlos a la ventana de su aplicación en la representación inicial.

Sin embargo, también debe recordar que el método onerror() puede ser innecesariamente difícil de configurar en proyectos más pequeños con un menor margen de error. Si está seguro de que su aplicación no arrojará demasiados errores, el método tradicional de lanzar/atrapar funcionará mejor para usted.

Devoluciones de llamadas y promesas

El manejo de errores en devoluciones de llamada y promesas difiere debido a su diseño y estructura de código. Sin embargo, si elige entre estos dos antes de haber escrito su código, sería mejor seguir con las promesas.

Esto se debe a que las promesas tienen una construcción incorporada para encadenar un bloque catch() y un bloque finally() para manejar los errores fácilmente. Este método es más fácil y limpio que definir argumentos adicionales/reutilizar argumentos existentes para manejar errores.

Keep Track of Changes With Git Repositories

Many errors often arise due to manual mistakes in the codebase. While developing or debugging your code, you might end up making unnecessary changes that may cause new errors to appear in your codebase. Automated testing is a great way to keep your code in check after every change. However, it can only tell you if something's wrong. If you don't take frequent backups of your code, you'll end up wasting time trying to fix a function or a script that was working just fine before.

This is where git plays its role. With a proper commit strategy, you can use your git history as a backup system to view your code as it evolved through the development. You can easily browse through your older commits and find out the version of the function working fine before but throwing errors after an unrelated change.

You can then restore the old code or compare the two versions to determine what went wrong. Modern web development tools like GitHub Desktop or GitKraken help you to visualize these changes side by side and figure out the mistakes quickly.

A habit that can help you make fewer errors is running code reviews whenever you make a significant change to your code. If you're working in a team, you can create a pull request and have a team member review it thoroughly. This will help you use a second pair of eyes to spot out any errors that might have slipped by you.

Best Practices for Handling Errors in JavaScript

The above-mentioned methods are adequate to help you design a robust error handling approach for your next JavaScript application. However, it would be best to keep a few things in mind while implementing them to get the best out of your error-proofing. Here are some tips to help you.

1. Use Custom Errors When Handling Operational Exceptions

We introduced custom errors early in this guide to give you an idea of how to customize the error handling to your application's unique case. It's advisable to use custom errors wherever possible instead of the generic Error class as it provides more contextual information to the calling environment about the error.

On top of that, custom errors allow you to moderate how an error is displayed to the calling environment. This means that you can choose to hide specific details or display additional information about the error as and when you wish.

You can go so far as to format the error contents according to your needs. This gives you better control over how the error is interpreted and handled.

2. Do Not Swallow Any Exceptions

Even the most senior developers often make a rookie mistake — consuming exceptions levels deep down in their code.

You might come across situations where you have a piece of code that is optional to run. If it works, great; if it doesn't, you don't need to do anything about it.

In these cases, it's often tempting to put this code in a try block and attach an empty catch block to it. However, by doing this, you'll leave that piece of code open to causing any kind of error and getting away with it. This can become dangerous if you have a large codebase and many instances of such poor error management constructs.

The best way to handle exceptions is to determine a level on which all of them will be dealt and raise them until there. This level can be a controller (in an MVC architecture app) or a middleware (in a traditional server-oriented app).

This way, you'll get to know where you can find all the errors occurring in your app and choose how to resolve them, even if it means not doing anything about them.

3. Use a Centralized Strategy for Logs and Error Alerts

Logging an error is often an integral part of handling it. Those who fail to develop a centralized strategy for logging errors may miss out on valuable information about their app's usage.

An app's event logs can help you figure out crucial data about errors and help to debug them quickly. If you have proper alerting mechanisms set up in your app, you can know when an error occurs in your app before it reaches a large section of your user base.

It's advisable to use a pre-built logger or create one to suit your needs. You can configure this logger to handle errors based on their levels (warning, debug, info, etc.), and some loggers even go so far as to send logs to remote logging servers immediately. This way, you can watch how your application's logic performs with active users.

4. Notify Users About Errors Appropriately

Another good point to keep in mind while defining your error handling strategy is to keep the user in mind.

All errors that interfere with the normal functioning of your app must present a visible alert to the user to notify them that something went wrong so the user can try to work out a solution. If you know a quick fix for the error, such as retrying an operation or logging out and logging back in, make sure to mention it in the alert to help fix the user experience in real-time.

In the case of errors that don't cause any interference with the everyday user experience, you can consider suppressing the alert and logging the error to a remote server for resolving later.

5. Implement a Middleware (Node.js)

The Node.js environment supports middlewares to add functionalities to server applications. You can use this feature to create an error-handling middleware for your server.

The most significant benefit of using middleware is that all of your errors are handled centrally in one place. You can choose to enable/disable this setup for testing purposes easily.

Here's how you can create a basic middleware:

const logError = err => { console.log("ERROR: " + String(err)) } const errorLoggerMiddleware = (err, req, res, next) => { logError(err) next(err) } const returnErrorMiddleware = (err, req, res, next) => { res.status(err.statusCode || 500) .send(err.message) } module.exports = { logError, errorLoggerMiddleware, returnErrorMiddleware }

Luego puede usar este middleware en su aplicación de esta manera:

 const { errorLoggerMiddleware, returnErrorMiddleware } = require('./errorMiddleware') app.use(errorLoggerMiddleware) app.use(returnErrorMiddleware)

Ahora puede definir una lógica personalizada dentro del middleware para manejar los errores de manera adecuada. Ya no necesita preocuparse por implementar construcciones de manejo de errores individuales en su base de código.

6. Reinicie su aplicación para manejar los errores del programador (Node.js)

Cuando las aplicaciones de Node.js encuentran errores del programador, es posible que no generen necesariamente una excepción e intenten cerrar la aplicación. Dichos errores pueden incluir problemas derivados de errores del programador, como un alto consumo de CPU, una sobrecarga de memoria o pérdidas de memoria. La mejor manera de manejar esto es reiniciar correctamente la aplicación bloqueándola a través del modo de clúster de Node.js o una herramienta única como PM2. Esto puede garantizar que la aplicación no se bloquee con la acción del usuario, presentando una experiencia de usuario terrible.

7. Captura todas las excepciones no detectadas (Node.js)

Nunca puede estar seguro de haber cubierto todos los posibles errores que pueden ocurrir en su aplicación. Por lo tanto, es esencial implementar una estrategia alternativa para capturar todas las excepciones no detectadas de su aplicación.

Así es como puedes hacer eso:

 process.on('uncaughtException', error => { console.log("ERROR: " + String(error)) // other handling mechanisms })

También puede identificar si el error que ocurrió es una excepción estándar o un error operativo personalizado. Según el resultado, puede salir del proceso y reiniciarlo para evitar un comportamiento inesperado.

8. Captura todos los rechazos de promesas no manejados (Node.js)

De manera similar a cómo nunca puede cubrir todas las posibles excepciones, existe una alta probabilidad de que se pierda el manejo de todos los posibles rechazos de promesas. Sin embargo, a diferencia de las excepciones, los rechazos de promesas no arrojan errores.

Por lo tanto, una promesa importante que fue rechazada podría pasar desapercibida como una advertencia y dejar su aplicación abierta a la posibilidad de encontrarse con un comportamiento inesperado. Por lo tanto, es crucial implementar un mecanismo alternativo para manejar el rechazo de promesas.

Así es como puedes hacer eso:

 const promiseRejectionCallback = error => { console.log("PROMISE REJECTED: " + String(error)) } process.on('unhandledRejection', callback)
Si crea una aplicación, hay posibilidades de que también cree errores y otros problemas. Aprende a manejarlos con la ayuda de esta guía ️ Haz clic para twittear

Resumen

Como cualquier otro lenguaje de programación, los errores son bastante frecuentes y naturales en JavaScript. En algunos casos, es posible que incluso deba arrojar errores intencionalmente para indicar la respuesta correcta a sus usuarios. Por lo tanto, comprender su anatomía y tipos es muy importante.

Además, debe estar equipado con las herramientas y técnicas adecuadas para identificar y evitar que los errores eliminen su aplicación.

En la mayoría de los casos, una estrategia sólida para manejar los errores con una ejecución cuidadosa es suficiente para todos los tipos de aplicaciones de JavaScript.

¿Hay algún otro error de JavaScript que aún no hayas podido resolver? ¿Alguna técnica para manejar los errores JS de manera constructiva? ¡Háganos saber en los comentarios a continuación!