Las promesas llegaron con ES6 para resolver uno de los problemas más frustrantes del desarrollo asíncrono en JavaScript: el callback hell. Ese código anidado, ilegible y difícil de mantener queda atrás cuando comprendes cómo funcionan las promesas, sus estados y la forma correcta de consumirlas.
¿Qué es una promesa y por qué cambia todo?
Una promesa es un objeto que representa el resultado eventual de una operación asíncrona [0:08]. No es el resultado en sí, sino la garantía de que ese resultado va a llegar o de que algo va a fallar. Una analogía útil: cuando pides comida a domicilio, la app te entrega un número de seguimiento. Ese número no es la comida, es la promesa de que te va a llegar.
Para crear una promesa se utiliza la palabra clave new Promise, que recibe dos argumentos: resolve y reject [0:45].
javascript
const miPromesa = new Promise((resolve, reject) => {
setTimeout(() => {
const exito = true;
if (exito) {
resolve('La operación fue exitosa');
} else {
reject('La operación falló');
}
}, 2000);
});
console.log(miPromesa);
Al ejecutar console.log(miPromesa) de forma inmediata, el resultado muestra una promesa en estado pending [1:10]. Esto sucede porque el setTimeout aún no ha terminado y la promesa todavía no se ha resuelto ni rechazado.
¿Cuáles son los tres estados de una promesa?
Toda promesa solo puede existir en uno de tres estados y solo transiciona en una dirección [1:18]:
- Pending: estado inicial, la operación aún no terminó.
- Fulfilled: la promesa se resolvió con éxito mediante
resolve.
- Rejected: la promesa falló y se ejecutó
reject.
Una vez que una promesa pasa a fulfilled o rejected, no puede cambiar de estado. Es inmutable [1:40]. Esta es una diferencia fundamental con los callbacks, donde nada impide llamar al callback dos veces.
javascript
const promesa = new Promise((resolve, reject) => {
resolve('Primer valor');
resolve('Segundo valor'); // ignorado
reject('Error'); // ignorado
});
Solo se respeta el primer resolve; todo lo demás es completamente ignorado [1:55].
¿Cómo se consumen las promesas con then y catch?
Para acceder al valor futuro que entrega una promesa se utilizan los métodos .then() y .catch() [2:10].
javascript
function obtenerNombre() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const hayError = true;
if (hayError) {
reject('Algo salió mal');
} else {
resolve('María');
}
}, 1000);
});
}
obtenerNombre()
.then((nombre) => console.log(Hola, ${nombre}))
.catch((error) => console.log(error));
- Cuando
hayError es true, se ejecuta .catch() e imprime "Algo salió mal" [2:40].
- Cuando
hayError es false, se ejecuta .then() e imprime "Hola, María" [2:50].
¿Cómo se aplican las promesas en una API real?
Un caso práctico es consumir una API con fetch, que retorna una promesa de forma nativa [3:00].
javascript
fetch('https://api-colombia.com/api/v1/Department')
.then((respuesta) => respuesta.json())
.then((departamentos) => {
console.log(departamentos[0].name);
departamentos.forEach((dep) => {
console.log(${dep.name} - Población: ${dep.population});
});
})
.catch((error) => console.log('Error consultando la API de Colombia', error));
El primer .then() convierte la respuesta a JSON. El segundo .then() accede a los datos y recorre cada departamento imprimiendo su nombre y población [3:20]. El resultado muestra que el primer departamento es Arauca, seguido de todos los demás con su respectiva población.
¿Qué ocurre cuando un error no se maneja correctamente?
Si se intenta acceder a un índice inexistente, como departamentos[35], se obtiene un TypeError porque no se pueden leer propiedades de undefined [3:55]. El bloque .catch() captura ese error y lo muestra con el mensaje personalizado.
Entender que una promesa nace en pending y termina en fulfilled o rejected sin posibilidad de retroceder es la base para trabajar con código asíncrono limpio y predecible. ¿Ya has tenido problemas con promesas encadenadas? Comparte tu experiencia en los comentarios.