Promises
Una Promise (promesa) es un objeto que representa el resultado eventual de una operación asíncrona. Las Promises hacen el código asíncrono más fácil de leer y mantener que los callbacks anidados.
¿Qué es una Promise?
Una Promise es un objeto que puede estar en uno de tres estados:
Pending (⏳ Pendiente): Estado inicial, la operación aún no ha terminado
Fulfilled (✅ Cumplida): La operación se completó exitosamente
Rejected (❌ Rechazada): La operación falló
const promise = new Promise (( resolve , reject ) => {
// Operación asíncrona aquí
});
console . log ( promise ); // Promise { <pending> }
Una vez que una Promise está fulfilled o rejected, su estado no puede cambiar. Se dice que está “settled” (resuelta).
Crear una Promise
Para crear una Promise, usas el constructor Promise con una función que recibe dos parámetros: resolve y reject.
Promise Resuelta (Fulfilled)
const resolvePromiseBtn = document . getElementById ( "promise-resolved" );
resolvePromiseBtn . addEventListener ( "click" , function () {
const promise = new Promise (( resolve , reject ) => {
setTimeout (() => {
resolve ( "✅ Operación exitosa" );
}, 2000 );
});
console . log ( promise );
promise
. then (( mensaje ) => console . log ( mensaje ))
. catch (( error ) => console . log ( error ));
});
Flujo de ejecución:
Se crea la Promise (estado: pending)
Se imprime la Promise en la consola (muestra Promise { <pending> })
Después de 2 segundos, se llama a resolve()
La Promise cambia a estado fulfilled
Se ejecuta el callback en .then()
Se imprime ”✅ Operación exitosa”
Promise Rechazada (Rejected)
const rejectPromiseBtn = document . getElementById ( "promise-rejected" );
rejectPromiseBtn . addEventListener ( "click" , function () {
const promise = new Promise (( resolve , reject ) => {
setTimeout (() => {
reject ( "❌ Hubo un error" );
}, 2000 );
});
promise
. then (( mensaje ) => console . log ( mensaje ))
. catch (( error ) => console . log ( error ));
});
Flujo de ejecución:
Se crea la Promise
Después de 2 segundos, se llama a reject()
La Promise cambia a estado rejected
Se salta el .then() y se ejecuta .catch()
Se imprime ”❌ Hubo un error”
Si una Promise es rejected y no tienes un .catch(), obtendrás un error “Unhandled Promise Rejection” en la consola.
Consumir Promises: .then() y .catch()
Una vez que tienes una Promise, la “consumes” con .then() y .catch():
promise
. then (( resultado ) => {
// Se ejecuta si la Promise se resuelve exitosamente
console . log ( 'Exitoso:' , resultado );
})
. catch (( error ) => {
// Se ejecuta si la Promise es rechazada
console . error ( 'Error:' , error );
});
Métodos de Promise
.then(onFulfilled, onRejected)
Recibe dos funciones opcionales:
La primera se ejecuta si la Promise es fulfilled
La segunda se ejecuta si la Promise es rejected
promise . then (
( resultado ) => console . log ( 'Exitoso:' , resultado ),
( error ) => console . error ( 'Error:' , error )
);
Retorna una nueva Promise , lo que permite encadenar.
Maneja errores. Es equivalente a .then(null, onRejected). promise . catch (( error ) => {
console . error ( 'Error:' , error );
});
También retorna una Promise, permitiendo encadenar más.
Se ejecuta siempre, sin importar si la Promise fue fulfilled o rejected. promise
. then ( resultado => console . log ( resultado ))
. catch ( error => console . error ( error ))
. finally (() => console . log ( 'Operación completada' ));
Útil para limpieza (ej: cerrar spinners de carga).
Encadenar Promises
Una de las grandes ventajas de las Promises es que puedes encadenarlas:
fetch ( '/api/user/1' )
. then (( response ) => {
console . log ( 'Respuesta recibida' );
return response . json (); // Retorna otra Promise
})
. then (( data ) => {
console . log ( 'Datos parseados:' , data );
return procesarDatos ( data ); // Retorna otra Promise
})
. then (( resultado ) => {
console . log ( 'Datos procesados:' , resultado );
})
. catch (( error ) => {
console . error ( 'Error en cualquier paso:' , error );
});
Ventajas sobre callbacks:
Código más plano (no hay pirámide de callbacks)
Un solo .catch() maneja errores de todos los pasos
Más fácil de leer y mantener
Cada .then() retorna una nueva Promise. Si retornas un valor, se envuelve automáticamente en una Promise resuelta.
Ejemplo: De Callbacks a Promises
Con Callbacks (Callback Hell)
getUser ( userId , ( error , user ) => {
if ( error ) {
console . error ( error );
return ;
}
getOrders ( user . id , ( error , orders ) => {
if ( error ) {
console . error ( error );
return ;
}
processOrders ( orders , ( error , result ) => {
if ( error ) {
console . error ( error );
return ;
}
console . log ( 'Resultado:' , result );
});
});
});
Con Promises
getUser ( userId )
. then ( user => getOrders ( user . id ))
. then ( orders => processOrders ( orders ))
. then ( result => console . log ( 'Resultado:' , result ))
. catch ( error => console . error ( error ));
¡Mucho más limpio y fácil de leer!
Promise.all(): Múltiples Promises en Paralelo
Cuando necesitas esperar a que varias Promises terminen:
const promesa1 = fetch ( '/api/users' );
const promesa2 = fetch ( '/api/products' );
const promesa3 = fetch ( '/api/orders' );
Promise . all ([ promesa1 , promesa2 , promesa3 ])
. then (([ usuarios , productos , ordenes ]) => {
console . log ( 'Todas las peticiones completadas' );
console . log ( 'Usuarios:' , usuarios );
console . log ( 'Productos:' , productos );
console . log ( 'Ordenes:' , ordenes );
})
. catch ( error => {
console . error ( 'Al menos una petición falló:' , error );
});
Características:
Espera a que todas las Promises se resuelvan
Si cualquiera falla, Promise.all() es rejected inmediatamente
Retorna un array con todos los resultados en el mismo orden
Otros Métodos de Promise
Promise.race()
Promise.allSettled()
Promise.any()
Retorna la Promise que termine primero (fulfilled o rejected). const promesa1 = new Promise ( resolve => setTimeout (() => resolve ( 'Lento' ), 2000 ));
const promesa2 = new Promise ( resolve => setTimeout (() => resolve ( 'Rápido' ), 1000 ));
Promise . race ([ promesa1 , promesa2 ])
. then ( resultado => console . log ( resultado )); // "Rápido"
Útil para timeouts o racing conditions. Espera a que todas las Promises terminen, sin importar si son fulfilled o rejected. const promesa1 = Promise . resolve ( 'Exitoso' );
const promesa2 = Promise . reject ( 'Error' );
const promesa3 = Promise . resolve ( 'Otro exitoso' );
Promise . allSettled ([ promesa1 , promesa2 , promesa3 ])
. then ( resultados => {
resultados . forEach ( resultado => {
if ( resultado . status === 'fulfilled' ) {
console . log ( 'Exitoso:' , resultado . value );
} else {
console . log ( 'Rechazado:' , resultado . reason );
}
});
});
Retorna la primera Promise que sea fulfilled. Ignora las rejected. const promesa1 = Promise . reject ( 'Error 1' );
const promesa2 = new Promise ( resolve => setTimeout (() => resolve ( 'Exitoso' ), 1000 ));
const promesa3 = Promise . reject ( 'Error 2' );
Promise . any ([ promesa1 , promesa2 , promesa3 ])
. then ( resultado => console . log ( resultado )); // "Exitoso"
Manejo de Errores
Un .catch() para Todos
fetch ( '/api/data' )
. then ( response => response . json ())
. then ( data => procesarDatos ( data ))
. then ( resultado => mostrarResultado ( resultado ))
. catch ( error => {
// Maneja errores de CUALQUIERA de los pasos anteriores
console . error ( 'Error en alguna parte:' , error );
});
.catch() en el Medio
fetch ( '/api/data' )
. then ( response => response . json ())
. catch ( error => {
console . error ( 'Error al obtener datos:' , error );
return { default: true }; // Valor por defecto
})
. then ( data => {
// Se ejecuta incluso si hubo error arriba
console . log ( 'Datos:' , data );
});
Si un .catch() no lanza un error, la cadena continúa normalmente. Esto permite recuperarse de errores.
Crear Promises de Valores
// Promise resuelta inmediatamente
const promesaResuelta = Promise . resolve ( 'Valor' );
// Promise rechazada inmediatamente
const promesaRechazada = Promise . reject ( new Error ( 'Error' ));
// Útil para convertir valores en Promises
function obtenerDatos ( cache ) {
if ( cache ) {
return Promise . resolve ( cache ); // Retorna Promise
}
return fetch ( '/api/data' ); // También retorna Promise
}
Buenas Prácticas
Siempre maneja errores
// ❌ Mal: Sin .catch()
fetch ( '/api/data' ). then ( handleData );
// ✅ Bien: Con .catch()
fetch ( '/api/data' )
. then ( handleData )
. catch ( handleError );
Retorna Promises en cadenas
// ❌ Mal: No retorna
promise . then (() => {
otraPromise (); // Esta Promise se ignora
});
// ✅ Bien: Retorna la Promise
promise . then (() => {
return otraPromise (); // Se encadena correctamente
});
Evita mezclar callbacks y Promises
// ❌ Confuso: Mezcla callbacks con Promises
fetch ( '/api/data' ). then ( data => {
setTimeout (() => {
procesarDatos ( data , ( resultado ) => {
// ...
});
}, 1000 );
});
// ✅ Mejor: Todo con Promises
fetch ( '/api/data' )
. then ( data => esperarUnSegundo ())
. then (() => procesarDatosPromise ( data ));
Próximos Pasos
Async/Await Aprende la sintaxis más moderna para trabajar con Promises
Fetch API Usa Promises para hacer peticiones HTTP