JS 18: Manejo de Errores

Anticipa, captura y gestiona errores como un profesional

1. ¿Qué son los Errores?

Los errores son problemas que ocurren durante la ejecución del código. Sin manejo adecuado, detienen la ejecución del programa.

Sin Manejo de Errores

function dividir(a, b) {

  return a / b;

}



console.log(dividir(10, 2)); // 5

console.log(dividir(10, 0)); // Infinity ??



// Código problemático

const usuario = null;

console.log(usuario.nombre); // ? Error: Cannot read property 'nombre' of null

console.log('Este código nunca se ejecuta'); // No se ejecuta

Tipos de Errores Comunes

2. try...catch - Capturar Errores

try...catch permite "intentar" ejecutar código y capturar errores sin romper el programa.

Sintaxis Básica

try {

  // Código que puede fallar

  const usuario = null;

  console.log(usuario.nombre);

} catch (error) {

  // Se ejecuta SOLO si hay error

  console.error('Ocurrió un error:', error.message);

}



console.log('El programa continúa'); // ? Se ejecuta

El Objeto Error

try {

  const x = y; // 'y' no está definida

} catch (error) {

  console.log('Nombre:', error.name);       // "ReferenceError"

  console.log('Mensaje:', error.message);   // "y is not defined"

  console.log('Stack:', error.stack);       // Traza del error

}

finally - Siempre se Ejecuta

function procesarArchivo() {

  let archivo = 'abierto';

  

  try {

    console.log('Procesando...');

    throw new Error('Error al procesar');

    console.log('Esto no se ejecuta');

  } catch (error) {

    console.log('Error capturado:', error.message);

  } finally {

    // Se ejecuta SIEMPRE (haya o no error)

    console.log('Cerrando archivo...');

    archivo = 'cerrado';

  }

  

  console.log('Archivo:', archivo); // "cerrado"

}



procesarArchivo();

// Procesando...

// Error capturado: Error al procesar

// Cerrando archivo...

// Archivo: cerrado

3. throw - Lanzar Errores

Puedes crear y lanzar tus propios errores con throw:

Lanzar Error Simple

function dividir(a, b) {

  if (b === 0) {

    throw new Error('No se puede dividir por cero');

  }

  return a / b;

}



try {

  console.log(dividir(10, 2)); // 5

  console.log(dividir(10, 0)); // Lanza error

} catch (error) {

  console.error('Error:', error.message); // "No se puede dividir por cero"

}

Lanzar Diferentes Tipos de Errores

function validarEdad(edad) {

  if (typeof edad !== 'number') {

    throw new TypeError('La edad debe ser un número');

  }

  

  if (edad < 0 || edad > 150) {

    throw new RangeError('Edad fuera de rango válido');

  }

  

  if (edad < 18) {

    throw new Error('Debes ser mayor de edad');

  }

  

  return 'Edad válida';

}



try {

  console.log(validarEdad('25'));  // TypeError

  // console.log(validarEdad(200));  // RangeError

  // console.log(validarEdad(15));   // Error

} catch (error) {

  if (error instanceof TypeError) {

    console.log('Error de tipo:', error.message);

  } else if (error instanceof RangeError) {

    console.log('Error de rango:', error.message);

  } else {

    console.log('Error general:', error.message);

  }

}

Lanzar Cualquier Valor

try {

  throw 'String error';

  // throw 404;

  // throw { codigo: 500, mensaje: 'Error del servidor' };

} catch (error) {

  console.log(error); // Captura cualquier cosa lanzada

}

4. Errores Person human alizados (Custom Errors)

Crea tus propias clases de error para mayor claridad:

Clase de Error Personalizada

class ErrorValidacion extends Error {

  constructor(mensaje) {

    super(mensaje);

    this.name = 'ErrorValidacion';

  }

}



class ErrorAutenticacion extends Error {

  constructor(mensaje) {

    super(mensaje);

    this.name = 'ErrorAutenticacion';

    this.codigo = 401;

  }

}



function login(usuario, password) {

  if (!usuario || !password) {

    throw new ErrorValidacion('Usuario y contraseña son requeridos');

  }

  

  if (password.length < 8) {

    throw new ErrorValidacion('La contraseña debe tener al menos 8 caracteres');

  }

  

  if (usuario !== 'admin' || password !== 'password123') {

    throw new ErrorAutenticacion('Credenciales incorrectas');

  }

  

  return 'Login exitoso';

}



try {

  console.log(login('admin', '123')); // Error

} catch (error) {

  if (error instanceof ErrorValidacion) {

    console.log('?? Validación:', error.message);

  } else if (error instanceof ErrorAutenticacion) {

    console.log('?? Auth error:', error.message, `(código ${error.codigo})`);

  } else {

    console.log('? Error desconocido:', error);

  }

}

5. Manejo de Errores Asíncronos

Con Promesas (.catch)

function obtenerUsuario(id) {

  return new Promise((resolve, reject) => {

    if (id < 0) {

      reject(new Error('ID inválido'));

    }

    setTimeout(() => {

      resolve({ id, nombre: 'Usuario ' + id });

    }, 1000);

  });

}



obtenerUsuario(5)

  .then(usuario => console.log(usuario))

  .catch(error => console.error('Error:', error.message));



obtenerUsuario(-1)

  .then(usuario => console.log(usuario))

  .catch(error => console.error('Error capturado:', error.message));

Con Async/Await (try...catch)

async function cargarUsuario(id) {

  try {

    const usuario = await obtenerUsuario(id);

    console.log('Usuario cargado:', usuario);

    return usuario;

  } catch (error) {

    console.error('Error al cargar:', error.message);

    return null;

  }

}



cargarUsuario(5);

cargarUsuario(-1);

Fetch con Manejo de Errores

async function obtenerDatos(url) {

  try {

    const response = await fetch(url);

    

    // fetch NO lanza error con códigos 404, 500, etc.

    if (!response.ok) {

      throw new Error(`HTTP ${response.status}: ${response.statusText}`);

    }

    

    const data = await response.json();

    return data;

    

  } catch (error) {

    // Error de red o HTTP error

    if (error.name === 'TypeError') {

      console.error('Error de red:', error.message);

    } else {

      console.error('Error:', error.message);

    }

    return null;

  }

}



obtenerDatos('https://jsonplaceholder.typicode.com/users/1');

obtenerDatos('https://api-inexistente.com/data');

6. Patrones de Manejo de Errores

1. Early Return (Retorno Temprano)

function procesarPedido(pedido) {

  if (!pedido) {

    throw new Error('Pedido inválido');

  }

  

  if (!pedido.items || pedido.items.length === 0) {

    throw new Error('El pedido debe tener items');

  }

  

  if (pedido.total < 0) {

    throw new Error('Total inválido');

  }

  

  // Lógica principal aquí

  return 'Pedido procesado';

}

2. Error First Callback (Node.js style)

function leerArchivo(nombre, callback) {

  setTimeout(() => {

    if (!nombre) {

      return callback(new Error('Nombre de archivo requerido'), null);

    }

    callback(null, 'Contenido del archivo');

  }, 1000);

}



leerArchivo('documento.txt', (error, contenido) => {

  if (error) {

    console.error('Error:', error.message);

    return;

  }

  console.log('Contenido:', contenido);

});

3. Result Object Pattern

function operacionSegura(valor) {

  try {

    if (valor < 0) {

      throw new Error('Valor debe ser positivo');

    }

    return { success: true, data: valor * 2, error: null };

  } catch (error) {

    return { success: false, data: null, error: error.message };

  }

}



const resultado1 = operacionSegura(5);

if (resultado1.success) {

  console.log('Resultado:', resultado1.data); // 10

}



const resultado2 = operacionSegura(-5);

if (!resultado2.success) {

  console.error('Error:', resultado2.error); // "Valor debe ser positivo"

}

4. Try-Catch Wrapper

// Envolver funciones async para manejo centralizado

function asyncHandler(fn) {

  return async function(...args) {

    try {

      return await fn(...args);

    } catch (error) {

      console.error(`Error en ${fn.name}:`, error.message);

      // Logging, notificación, etc.

      throw error; // Re-lanzar si es necesario

    }

  };

}



const obtenerUsuarioSeguro = asyncHandler(async function obtenerUsuario(id) {

  const response = await fetch(`https://api.example.com/users/${id}`);

  return await response.json();

});



obtenerUsuarioSeguro(1);

7. Mejores Prácticas

? Hacer

? Evitar

Ejemplos

// ? MAL - Catch vacío

try {

  funcionPeligrosa();

} catch (e) {

  // Silencia el error - muy peligroso

}



// ? MAL - Lanzar strings

throw 'algo salió mal'; // Pierde stack trace



// ? BIEN - Error específico y descriptivo

if (!usuario.email) {

  throw new ErrorValidacion('El email es requerido para crear usuario');

}



// ? BIEN - Logging antes de re-lanzar

try {

  procesarDatos(datos);

} catch (error) {

  console.error('Error al procesar:', error);

  // Notificar a servicio de monitoreo

  throw error; // Re-lanzar para que se maneje arriba

}

8. Debugging de Errores

1. Console Avanzado

try {

  throw new Error('Error de prueba');

} catch (error) {

  console.error(error);                // Error completo

  console.error(error.message);        // Solo mensaje

  console.error(error.stack);          // Stack trace

  console.table({ name: error.name, message: error.message });

}

2. Breakpoints con debugger

function funcionProblematica(x) {

  if (x < 0) {

    debugger; // El navegador se pausará aquí

    throw new Error('Valor negativo');

  }

  return x * 2;

}

3. Capturar Errores No Manejados

// Errores síncronos globales

window.addEventListener('error', (event) => {

  console.error('Error global:', event.error);

  // Enviar a servicio de logging

});



// Promesas rechazadas no manejadas

window.addEventListener('unhandledrejection', (event) => {

  console.error('Promesa no manejada:', event.reason);

  event.preventDefault(); // Evitar que se muestre en consola

});

9. Ejercicios Prácticos

Ejercicio 1: Validador de Email

function validarEmail(email) {

  // Debe lanzar TypeError si no es string

  // Debe lanzar Error si está vacío

  // Debe lanzar Error si no contiene '@'

  // Debe retornar true si es válido

}



try {

  console.log(validarEmail('test@example.com')); // true

  console.log(validarEmail('invalido'));         // Error

} catch (error) {

  console.error(error.message);

}

Ejercicio 2: Función con Reintentos

async function intentarOperacion(operacion, intentos = 3) {

  // Intenta ejecutar 'operacion' hasta 'intentos' veces

  // Si falla todas las veces, lanza el último error

}



// Probar con una función que falla aleatoriamente

async function operacionInestable() {

  if (Math.random() < 0.7) {

    throw new Error('Falló');

  }

  return 'Éxito';

}



intentarOperacion(operacionInestable)

  .then(resultado => console.log(resultado))

  .catch(error => console.error('Falló después de 3 intentos'));

Resumen

Volver a JS