Ejecutar promesas de forma secuencial funciona, pero cuando las operaciones son independientes entre sí, el rendimiento se resiente innecesariamente. Si tres promesas tardan 300, 200 y 400 milisegundos respectivamente, en secuencia el total sería 900 milisegundos. En paralelo, solo tardaría lo que dure la más lenta: 400 milisegundos. Esta diferencia es fundamental para el performance de cualquier aplicación. JavaScript ofrece cuatro combinadores de promesas que permiten coordinar múltiples operaciones asíncronas con comportamientos distintos según lo que necesites.
¿Cómo funciona Promise.all para ejecutar promesas en paralelo?
Promise.all recibe un array de promesas y devuelve una nueva promesa que se resuelve únicamente cuando todas se resuelven con éxito [0:56]. Si una sola rechaza, toda la operación rechaza. Es un enfoque de todo o nada.
En el ejemplo práctico se realizan tres peticiones a la Platzi Fake Store API para obtener productos con distintos IDs. Si todas las peticiones son exitosas, se obtiene un array con los resultados y se puede iterar con forEach para mostrar el título y precio de cada producto [1:18].
javascript
Promise.all([
fetchProduct(4),
fetchProduct(5),
fetchProduct(6)
])
.then(products => {
products.forEach(p => console.log(p.title, p.price));
})
.catch(error => console.error(error));
Pero si una sola de las promesas falla, todas fallan [2:04]. Esto es útil cuando necesitas todos los resultados para continuar: por ejemplo, cargar el ID, nombre y email de un usuario en una pantalla donde todos los datos son obligatorios.
¿Cuándo usar Promise.allSettled en lugar de Promise.all?
Promise.allSettled espera a que todas las promesas terminen sin importar si se resolvieron o rechazaron [2:30]. Devuelve un array de objetos que describen el resultado de cada una, con un campo status que indica fulfilled o rejected.
javascript
Promise.allSettled([
Promise.resolve("Éxito"),
Promise.reject("Falló"),
Promise.resolve("Éxito también")
]).then(results => {
results.forEach(r => {
if (r.status === "fulfilled") console.log(r.value);
else console.log("Error:", r.reason);
});
});
El caso de uso ideal es cuando tienes operaciones independientes y quieres conocer el resultado de cada una sin cancelar las demás [3:15]. Un ejemplo claro: enviar notificaciones a múltiples usuarios y saber cuáles llegaron y cuáles no.
¿Qué hace Promise.race y cómo implementar timeouts?
Promise.race devuelve el resultado de la primera promesa que termine, sin importar si fue exitosa o falló [3:30]. Las demás se ignoran por completo.
javascript
const rapida = new Promise(resolve => setTimeout(() => resolve("Rápida"), 100));
const lenta = new Promise(resolve => setTimeout(() => resolve("Lenta"), 500));
const mediana = new Promise(resolve => setTimeout(() => resolve("Mediana"), 300));
Promise.race([rapida, lenta, mediana])
.then(ganadora => console.log(ganadora)); // "Rápida"
Un uso práctico muy potente es implementar timeouts [4:05]. Se crea una promesa que rechaza tras cierto tiempo y se compite contra la petición real. Si la petición no responde dentro del límite, el timeout gana y se muestra el error. Al poner 100 milisegundos como límite, la API no alcanza a responder. Cambiándolo a un segundo, la petición llega a tiempo y se obtienen los datos correctamente [4:40].
¿Para qué sirve Promise.any y en qué se diferencia de race?
Promise.any devuelve la primera promesa que se resuelva exitosamente [5:05]. A diferencia de Promise.race, ignora los rechazos individuales. Solo rechaza si todas las promesas fallan, generando un AggregateError.
javascript
Promise.any([
Promise.reject("Falló"),
Promise.resolve("Éxito"),
Promise.resolve("También éxito")
]).then(resultado => console.log(resultado)); // "Éxito"
Si todas fallan, ahí sí se produce un error completo [5:35]. El caso de uso principal es la redundancia con fallback automático: intentar la misma operación contra varios endpoints y quedarte con la primera respuesta exitosa.
¿Cuál combinador de promesas elegir según tu caso de uso?
- Promise.all: necesitas todos los resultados y si uno falla, falla todo.
- Promise.allSettled: quieres saber el estado individual de cada promesa, sin cancelar ninguna.
- Promise.race: necesitas implementar timeouts o elegir el servidor más rápido.
- Promise.any: buscas redundancia, quedándote con la primera respuesta exitosa.
Con estos cuatro combinadores se completa el toolkit de promesas: ciclo de vida, encadenamiento, manejo de errores y coordinación en paralelo [6:20]. La regla de oro es simple: si las operaciones son independientes, ejecútalas en paralelo. Si una depende de otra, mantén la secuencia. ¿Ya tienes claro cuál combinador usarías en tu próximo proyecto? Comparte tu caso de uso en los comentarios.