Manejo de errores en JavaScript: try/catch y técnicas avanzadas en Node.js

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

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 instanceof y 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