Resumen

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.