Manejo de Errores en JavaScript: Try / Catch y Más
¿Por qué es esencial el manejo de errores?
En aplicaciones modernas, un error inesperado puede provocar caídas, pérdida de datos o vulnerabilidades de seguridad. Un manejo estructurado permite:
- Mantener la experiencia de usuario fluida.
- Registrar información útil para depuración.
- Prevenir la exposición de datos sensibles.
- Facilitar la recuperación automática de operaciones críticas.
Sintaxis básica de try / catch
try {
// Código que puede lanzar una excepción
const resultado = operacionCritica();
console.log('Éxito:', resultado);
} catch (error) {
// Manejo centralizado del error
console.error('Se produjo un error:', error.message);
}
El bloque finally es opcional y se ejecuta siempre, ideal para liberar recursos.
try {
// …
} catch (e) {
// …
} finally {
// Cierre de conexiones, limpieza de timers, etc.
}
Comparativa rápida (dos columnas)
Try / Catch (síncrono)
- Captura errores lanzados de forma inmediata.
- Ideal para código que se ejecuta en el mismo stack.
- No captura rechazos de promesas sin
awaito.catch(). - Puede impactar el rendimiento si se usa en bucles críticos.
Promesas / Async‑Await
- Los rechazos se manejan con
.catch()otry/catchusandoawait. - Mejor para operaciones I/O y llamadas a APIs.
- Permite encadenar flujos sin anidar bloques
try. - Requiere atención a errores no manejados (unhandled promise rejections).
Ejemplos prácticos
1️⃣ Validación de entrada con throw
function dividir(a, b) {
if (b === 0) {
throw new Error('División por cero no permitida');
}
return a / b;
}
try {
console.log(dividir(10, 0));
} catch (e) {
console.warn('Operación inválida:', e.message);
}
2️⃣ Manejo de errores en código asíncrono con await
async function obtenerUsuario(id) {
const respuesta = await fetch(`https://api.example.com/users/${id}`);
if (!respuesta.ok) {
throw new Error(`HTTP ${respuesta.status}`);
}
return respuesta.json();
}
(async () => {
try {
const usuario = await obtenerUsuario(123);
console.log('Usuario:', usuario);
} catch (err) {
console.error('Error al cargar el usuario:', err.message);
}
})();
3️⃣ Creación de clases de error personalizadas
class ValidationError extends Error {
constructor(message, campo) {
super(message);
this.name = 'ValidationError';
this.campo = campo;
}
}
function validarUsuario(usuario) {
if (!usuario.email.includes('@')) {
throw new ValidationError('Formato de email inválido', 'email');
}
}
try {
validarUsuario({ email: 'bademail' });
} catch (e) {
if (e instanceof ValidationError) {
console.log(`Error en campo ${e.campo}: ${e.message}`);
}
}
Patrones avanzados
- Retry con backoff exponencial: envolver la lógica en una función que reintente tras fallos transitorios.
- Decoradores de manejo de errores: usar funciones de orden superior para envolver callbacks y evitar código repetitivo.
- Captura global:
window.onerroryprocess.on('uncaughtException')para registrar errores no capturados.
function conRetry(fn, intentos = 3, delay = 100) {
return async (...args) => {
for (let i = 0; i < intentos; i++) {
try {
return await fn(...args);
} catch (e) {
if (i === intentos - 1) throw e;
await new Promise(r => setTimeout(r, delay * 2 ** i)); // backoff
}
}
};
}
const fetchConRetry = conRetry(fetch);
Consideraciones de seguridad
Exponer la información del stack o mensajes de error a usuarios finales puede revelar detalles internos. Buenas prácticas:
- Loggear el error completo en el servidor y mostrar al usuario un mensaje genérico.
- Never
console.log(error)en producción; usar herramientas como Winston o Bunyan. - Sanitizar cualquier dato que provenga del error antes de enviarlo a la UI.
Testing y depuración
Utiliza jest o mocha para validar que los bloques try/catch se comporten como se espera.
test('debe lanzar ValidationError', () => {
expect(() => validarUsuario({ email: 'bad' }))
.toThrow(ValidationError);
});
Herramientas de depuración como Chrome DevTools permiten añadir “Break on exceptions” para detener la ejecución justo cuando ocurre el error.
Rendimiento y escalabilidad
El uso indiscriminado de try/catch dentro de bucles de alta frecuencia puede degradar el rendimiento porque el motor debe preparar una tabla de manejo de excepciones. Recomendaciones:
- Validar condiciones antes de entrar al bloque
trysiempre que sea posible. - En loops críticos (p.ej., procesamiento de streams), mover la captura a nivel superior.
- Medir con
performance.now()o herramientas de profiling antes y después de introducir manejo de errores.
Manejo de Errores en JavaScript: Guía Completa de Try / Catch y Buenas Prácticas