Manejo de errores en JavaScript: try/catch y más allá: nivel avanzado
El manejo de errores no es solo proteger la aplicación de fallos, sino un aspecto fundamental para construir sistemas robustos y tolerantes a fallos en JavaScript y Node.js. El uso básico de try/catch es familiar para todo desarrollador, pero en proyectos reales, especialmente del lado del servidor, surgen muchos matices: flujos asíncronos, cadenas de promesas, fallos globales y la necesidad de errores personalizados estructurados. En este artículo, analizaremos técnicas avanzadas de manejo de errores que te ayudarán a escribir código más limpio y predecible en Node.js.
1. Inmersión profunda en try/catch y asincronía
El try/catch clásico es síncrono. Captura perfectamente errores dentro de funciones normales, pero falla ante callbacks asíncronos o promesas sin await. En Node.js, donde el 90% del código es asíncrono, esto suele causar excepciones "tragadas".
Error: try/catch no captura el error en setTimeout
function dangerousAsync() { setTimeout(() => { throw new Error('¡Fallo en el temporizador!'); }, 1000);}
try { dangerousAsync();} catch (e) { console.log('Capturado:', e.message); // Este código NO se ejecutará}En este ejemplo, el error se lanza fuera del contexto de try y el proceso de Node.js terminará abruptamente. El enfoque correcto es usar promesas o async/await.
Solución: envolver el código asíncrono en una Promise
function safeAsync() { return new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('¡Fallo en el temporizador!')); }, 1000); });}
(async () => { try { await safeAsync(); } catch (e) { console.log('Capturado:', e.message); // Se ejecutará después de 1 segundo }})();La regla principal: envuelve cualquier operación asíncrona en una promesa y usa await dentro de try/catch. Esto garantiza que el error sea capturado en el contexto correcto.
2. Clases de error personalizadas: semántica y contexto
En lugar de lanzar new Error('Algo salió mal'), crea una jerarquía de tus propias clases de error. Esto permite que el manejador entienda el tipo de fallo y reaccione adecuadamente (por ejemplo, registrar, enviar a monitoreo o devolver un código HTTP específico al usuario).
class AppError extends Error { constructor(message, statusCode = 500) { super(message); this.name = this.constructor.name; this.statusCode = statusCode; Error.captureStackTrace(this, this.constructor); }}
class ValidationError extends AppError { constructor(message) { super(message, 400); }}
class DatabaseError extends AppError { constructor(message, originalError) { super(message, 503); this.originalError = originalError; // Guarda el error original para depuración }}
// Usofunction findUser(id) { if (!id) throw new ValidationError('El ID de usuario es obligatorio'); // ... lógica que podría lanzar DatabaseError}Ventajas de este enfoque:
- Legibilidad: el nombre de la clase indica inmediatamente la naturaleza del problema.
- Enrutamiento: en el manejador global puedes verificar
instanceofy elegir la estrategia de respuesta. - Campos adicionales: puedes agregar cualquier dato (
statusCode,originalError,userId, etc.).
3. Manejadores globales de errores en Node.js
Incluso con el control más meticuloso, los errores no manejados pueden filtrarse. En Node.js hay dos eventos clave para capturarlos: uncaughtException y unhandledRejection
Blogs
Book Recommendations
Leer también
API en JavaScript: REST, GraphQL, gRPC — ¿qué elegir en 2025?
Abrir enlaces en una nueva pestaña, no en una ventana: guía 2025
Cómo verificar si un elemento está en std::vector? Guía completa
Trabajo con JSON en TypeScript: análisis y serialización para principiantes
Documentación en PHP: cómo escribir código que no dé vergüenza mostrar