Manejo avanzado de errores en JavaScript: desde try/catch hasta interceptores globales
\n\nEl manejo de errores no es simplemente "envolver el código en try/catch". En aplicaciones reales de producción en Node.js, una gestión adecuada de excepciones permite evitar caídas del servidor, registrar problemas correctamente y proporcionar al usuario mensajes claros. En este artículo, exploraremos técnicas avanzadas de manejo de errores en JavaScript que van más allá de la sintaxis básica.
\n\n1. Inmersión profunda en try/catch y finally
\n\nLa construcción clásica try...catch...finally sigue siendo la base del manejo síncrono. Sin embargo, a nivel avanzado es importante comprender los matices de su funcionamiento.
function riskyOperation(value) {\n if (typeof value !== 'number') {\n throw new TypeError('El argumento debe ser un número');\n }\n if (value < 0) {\n throw new RangeError('El valor no puede ser negativo');\n }\n return Math.sqrt(value);\n}\n\ntry {\n const result = riskyOperation(-4);\n console.log('Resultado:', result);\n} catch (error) {\n if (error instanceof TypeError) {\n console.error('Error de tipo:', error.message);\n } else if (error instanceof RangeError) {\n console.error('Error de rango:', error.message);\n } else {\n console.error('Error desconocido:', error);\n }\n} finally {\n console.log('Limpieza: siempre se ejecuta');\n}\n\nPuntos clave:
\n- \n
- El bloque
finallysiempre se ejecuta, incluso si hay unreturnentryocatch. \n - Utilice las clases de error integradas (
TypeError,RangeError,SyntaxError) para una identificación precisa. \n - No capture errores que no pueda manejar; déjelos ascender. \n
2. Clases de error personalizadas y extensión de Error
\n\nPara proyectos grandes, los tipos de error estándar no son suficientes. Crear clases propias permite agregar metadatos (código de estado HTTP, código de error, contexto) y simplifica el filtrado.
\n\nclass AppError extends Error {\n constructor(message, statusCode, code) {\n super(message);\n this.name = 'AppError';\n this.statusCode = statusCode;\n this.code = code;\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\nclass ValidationError extends AppError {\n constructor(message, field) {\n super(message, 400, 'VALIDATION_ERROR');\n this.field = field;\n }\n}\n\nclass DatabaseError extends AppError {\n constructor(message, query) {\n super(message, 500, 'DB_ERROR');\n this.query = query;\n }\n}\n\n// Uso\ntry {\n throw new ValidationError('El correo electrónico es obligatorio', 'email');\n} catch (error) {\n if (error instanceof ValidationError) {\n console.log(`Campo: ${error.field}, Estado: ${error.statusCode}`);\n }\n}\n\nPor qué es importante: los errores personalizados permiten manejar diferentes escenarios de forma centralizada (por ejemplo, en middleware de Express.js) y devolver un JSON estructurado al cliente.
\n\n3. Manejo de errores en código asíncrono: async/await y Promise
\n\nEn Node.js, la mayor parte del código es asíncrono. Los errores en Promise y async/await requieren un enfoque especial.
3.1. Manejo de Promise
\n\nfunction fetchUserData(userId) {\n return new Promise((resolve, reject) => {\n if (userId <= 0) {\n reject(new RangeError('ID de usuario no válido'));\n }\n // simulación de consulta a BD\n setTimeout(() => resolve({ id: userId, name: 'Alice' }), 100);\n });\n}\n\nfetchUserData(-1)\n .then(user => console.log(user))\n .catch(error => {\n console.error('Promise rechazada:', error.message);\n })\n .finally(() => console.log('Limpieza después de la promesa'));\n\n3.2. Manejo de async/await
\n\nasync function getUserProfile(userId) {\n try {\n const user = await fetchUserData(userId);\n const posts = await fetchUserPosts(user.id