Gestión de Errores#

En JavaScript, es crucial capturar y manejar errores para asegurar que el código continúe funcionando adecuadamente, incluso cuando algo falla. En este capítulo, exploraremos cómo manejar errores utilizando diferentes métodos y técnicas disponibles en JavaScript.

Cuando algo falla en JavaScript, el hilo de ejecución se detiene y deja de funcionar. Para evitar que esto ocurra, es posible capturar y manejar errores conocidos.

Objeto Error#

Cuando algo falla, se lanza un objeto Error. Este objeto contiene propiedades específicas como message, name, fileName, lineNumber, columnNumber, y stack. Además, tiene un método útil llamado toString().

Es posible lanzar errores personalizados utilizando el constructor de Error. Aunque Error es genérico, también existen errores más específicos como EvalError, InternalError, SyntaxError, RangeError, TypeError y URIError.

try {
    throw new Error('¡Ups!')
} catch (e) {
    console.error(e.name + ': ' + e.message)
}
Error: ¡Ups!

Manejar Errores#

Los errores pueden ser manejados de forma genérica o específica usando instanceof. Además, es posible crear errores personalizados. Si es necesario ejecutar código independientemente de si ocurre un error, se utiliza finally().

try {
    foo.bar();
} catch (e) {
    console.error(e.toString());
    if (e instanceof EvalError) {
        console.error(e.name + ': ' + e.message);
    } else if (e instanceof RangeError) {
        console.error(e.name + ': ' + e.message);
    } // ... etc
} finally {
    closeMyFile();
}
ReferenceError: foo is not defined
Stack trace:
ReferenceError: closeMyFile is not defined
    at <anonymous>:11:3

Errores en Promesas#

Cuando una promesa no puede ser cumplida, se ejecuta la función reject(), la cual puede ser capturada en el bloque .catch(). Sin embargo, los errores en promesas no pueden ser capturados directamente con try...catch debido a que .catch() siempre retorna otra promesa.

let promesa3 = new Promise(function promesa(resolve, reject) {
    try { 
        if (Math.random() > 0.5) resolve('Funciona 3');
        else throw new Error('No funciona 3');
    } catch (error) {
        reject(error.message);
    }
});

promesa3.then(function r(message) {
    console.log(message);
}).catch(function c(error) {
    throw new Error(error);
}).catch(function c(error) {
    console.error(error.toString());
});
Error: No funciona 3
Promise { undefined }

Errores en Async/Await#

Cuando se utiliza async/await, se pueden manejar errores dentro de un bloque try...catch. Si una promesa es rechazada (reject), await lanza un error que puede ser capturado con catch.

async function getData(url) {
    try {
        const response = await fetch(url);
        if (!response.ok) {
            throw new Error(`Error de servidor: ${response.status} ${response.statusText}`);
        }
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Error:', error.message);
        throw error;
    }
}

async function fetchExample() {
    const url = 'https://api.foo.com/data';
    try {
        const data = await getData(url);
        console.log('Datos recibidos:', data);
    } catch (error) {
        console.error('Error en la solicitud:', error);
    }
}

fetchExample();
Promise { <pending> }

Errores en Observables#

Los Observables pueden manejar errores ejecutando la función error() del Observer. Además, pueden retornar un error usando throwError() y este error puede ser capturado con catchError() dentro de las pipes.

import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

const observable = new Observable(subscriber => {
    try {
        subscriber.next('Next value');
        throw new Error('Something went wrong');
    } catch (error) {
        subscriber.error(error);
    }
});

observable.pipe(
    catchError(error => {
        console.error('Caught error:', error);
        return throwError(error); // Propaga el error
    })
).subscribe({
    next(value) { console.log(value); },
    error(err) { console.error('Error handler:', err); }
});

En caso de que un Observable falle, es posible reintentar una cantidad limitada de veces utilizando retry().

import { retry } from 'rxjs/operators';

observable.pipe(
    retry(3),
    catchError(error => {
        console.error('Caught error after retries:', error);
        return throwError(error);
    })
).subscribe({
    next(value) { console.log(value); },
    error(err) { console.error('Error handler:', err); }
});