El error que "vuela" en otro momento
Manejar errores en código asíncrono es distinto porque el fallo no ocurre en la línea donde escribiste la llamada, sino más tarde, cuando la promesa se resuelve. Hay dos herramientas principales.
1. try/catch con await
Dentro de una función async, await convierte un rechazo en una
excepción que puedes atrapar con try...catch normal, como si fuera código
síncrono:
async function cargar() {
try {
const datos = await pedirDatos(); // si se rechaza, salta al catch
return datos;
} catch (error) {
console.log("Falló:", error.message);
return null; // valor por defecto
}
}
2. .catch() encadenado
Sin async/await, se usa .catch() al final de la cadena:
pedirDatos()
.then((datos) => usar(datos))
.catch((error) => console.log("Falló:", error.message));
El error clásico: try/catch SIN await
Esto NO funciona como esperas:
async function malo() {
try {
pedirDatos(); // ¡falta el await!
} catch (error) {
console.log("Esto nunca se ejecuta");
}
}
Sin await, la línea pedirDatos() solo crea la promesa y sigue
adelante; el try ya terminó cuando, mucho después, la promesa se rechaza. El
catch síncrono no puede atrapar un fallo que ocurre fuera de su tiempo de
vida. Es como cerrar la red de seguridad antes de que el trapecista salte.
Regla: un
try/catchsolo atrapa el rechazo de una promesa si ponesawaitdelante (o si devuelves la promesa y manejas el error con.catch).
Rechazos no capturados
Si una promesa se rechaza y nadie la maneja (ni await en un try, ni un
.catch), se produce un unhandled rejection. En el navegador verás un
warning en consola; en Node, por defecto, termina el proceso. Por eso toda
promesa que pueda fallar debería tener su manejador de error.
// ❌ Rechazo sin manejar: warning / proceso caído
Promise.reject(new Error("nadie me atrapa"));
// ✅ Manejado
Promise.reject(new Error("ya me atrapan")).catch((e) => console.log(e.message));
Ejemplos
try/catch con await devuelve un valor por defecto
function fallar() {
return Promise.reject(new Error("boom"));
}
async function seguro() {
try {
return await fallar();
} catch (e) {
return "valor por defecto";
}
}
seguro().then((v) => console.log(v)); // "valor por defecto"
Sin await, el catch NO atrapa el rechazo
function fallar() {
return Promise.reject(new Error("boom"));
}
async function malo() {
try {
const p = fallar(); // sin await
p.catch(() => {}); // lo silenciamos para no romper la demo
return "el try terminó sin atrapar nada";
} catch (e) {
return "esto NO se ejecuta";
}
}
malo().then((v) => console.log(v));