Manejo de errores en promesas de JavaScript
Aprende el manejo de errores en promesas de JavaScript: .catch(), propagación de rechazos, relanzamiento, recuperación, .finally() y unhandledrejection.
Las promesas de JavaScript son una parte fundamental para gestionar operaciones asíncronas, y permiten a los desarrolladores manejar eventos asíncronos con más flexibilidad y facilidad (véase JavaScript: Promesas). El manejo de errores en las promesas es esencial para escribir código JavaScript robusto que pueda lidiar con problemas inesperados sin que la aplicación falle.
Este capítulo cubre cómo se capturan los rechazos, cómo fluyen los errores a través de una cadena, cómo relanzar y recuperarse, el gancho de limpieza .finally(), la red de seguridad global unhandledrejection, y cómo los mismos patrones se aplican a async/await. Si eres nuevo en el encadenamiento de llamadas .then(), lee primero Encadenamiento de promesas.
Una promesa puede terminar en uno de dos estados: fulfilled (produjo un valor) o rejected (algo salió mal). Un rechazo ocurre cuando llamas a reject(...), cuando lanzas una excepción dentro de un ejecutor o de un callback .then(), o cuando una API asíncrona nativa falla. El manejo de errores consiste en redirigir esos rechazos hacia un manejador en lugar de dejar que bloqueen el programa.
El manejo de errores en las promesas se realiza usando el método .catch() o pasando un segundo argumento al método .then(). Ambos métodos ofrecen maneras de gestionar y recuperarse de los errores que ocurren durante la ejecución de operaciones asíncronas.
Usando el método .catch()
El método .catch() se usa para capturar cualquier error que ocurra durante la ejecución de la cadena de promesas.
Usando el segundo argumento de .then()
Alternativamente, se puede pasar un segundo argumento a .then() para manejar los errores que ocurren después de que el callback del primer argumento se ejecuta.
Técnicas avanzadas de manejo de errores
Flujo de errores en cadenas
Los errores deben propagarse correctamente a través de la cadena de promesas para garantizar que se manejen en el nivel adecuado. Por ejemplo, si colocas el bloque .catch() antes del bloque .then(), el bloque .then() seguirá ejecutándose. Dado que .catch() resuelve la cadena de promesas (a menos que vuelva a lanzar un error), el siguiente .then() recibe undefined como argumento.
Cuando colocas el bloque .catch() antes del bloque .then(), cualquier error lanzado dentro del .then() no será capturado por el .catch() precedente. Solo se manejará si añades otro bloque .catch() después de él.
Propagación de rechazos
No es necesario poner un .catch() después de cada .then(). Un rechazo omite todos los manejadores de éxito (el primer argumento de .then()) y viaja por la cadena hasta que encuentra el primer .catch(). Esto permite escribir una cadena larga de pasos y manejar cualquier fallo en un único punto al final.
En el ejemplo siguiente, el rechazo ocurre al inicio, pero ninguno de los tres callbacks .then() se ejecuta — el control salta directamente al único .catch():
Relanzamiento y recuperación
Un manejador .catch() realiza dos funciones según lo que haga:
- Recuperar — si devuelve un valor (o no devuelve nada), la cadena vuelve a estar en estado fulfilled y el siguiente
.then()se ejecuta con ese valor. Así es como se proporciona un valor de respaldo. - Relanzar — si hace un
throw(o devuelve una promesa rechazada), el error sigue propagándose hacia el siguiente.catch(). Úsalo cuando un manejador no puede resolver el error completamente y quiere que un manejador posterior termine el trabajo.
Manejo de errores específicos
JavaScript permite estrategias de manejo de errores más detalladas, como filtrar errores según su tipo o las circunstancias específicas del error. En el siguiente ejemplo manejamos un TypeError. Un TypeError ocurre típicamente cuando un valor no es del tipo esperado y, por lo tanto, la operación deseada no puede realizarse.
Mejores prácticas para el manejo de errores en promesas
- Siempre devuelve o lanza errores en los bloques catch para asegurarte de que los errores no queden ignorados silenciosamente.
- Encadena las promesas correctamente para garantizar que los errores se capturen y manejen.
- Usa bloques finally cuando sea necesario para realizar tareas de limpieza, independientemente del resultado de la promesa.
Implementar un bloque finally
El método finally() se usa para ejecutar un bloque de código después de que las promesas se resuelven, independientemente del resultado.
Capturar rechazos no manejados
Si una promesa es rechazada y ningún .catch() la maneja, el error se pierde silenciosamente — no hay un try/catch envolvente como tendría el código síncrono. Para evitar que los errores desaparezcan, el entorno lanza un evento global unhandledrejection al que puedes suscribirte. Este es un último recurso para registro y reporte, no un reemplazo de un .catch() real.
En el navegador:
En Node.js el equivalente es process.on('unhandledrejection', (reason) => { ... }).
Manejo de errores con async/await
async/await está construido sobre promesas, por lo que los mismos rechazos ocurren — pero se capturan con un bloque try/catch ordinario, que se lee como código síncrono. Un await sobre una promesa rechazada lanza una excepción dentro de la función async, y try/catch la intercepta. Consulta JavaScript: async/await para ver el panorama completo.
Un try/catch dentro de una función async solo captura errores de las promesas que awaitas. Si llamas a una función que devuelve una promesa sin await, su rechazo escapa al try/catch — debes awaitarla o adjuntarle un .catch().
Conclusión
El manejo efectivo de errores en las promesas de JavaScript es esencial para desarrollar aplicaciones web confiables y resilientes. Al comprender el método .catch(), el segundo argumento de .then(), la propagación de rechazos, el relanzamiento y el gancho de limpieza .finally(), los desarrolladores pueden asegurarse de que sus aplicaciones manejen los errores asíncronos con elegancia y se recuperen en lugar de fallar. Cuando trabajes con async/await, envuelve las llamadas de promesas con await en bloques try/catch para un manejo de errores de apariencia síncrona, y mantén un listener global unhandledrejection como red de seguridad para los casos que puedas pasar por alto.
Para profundizar, continúa con Encadenamiento de promesas y la API de Promise.