Promises avanzadas en JavaScript: Promise.all, allSettled, race, any y patrones de composición

Lanzar varias peticiones asíncronas en paralelo es uno de los casos más comunes en aplicaciones JavaScript reales. Los cuatro métodos estáticos de Promise —all, allSettled, race y any— resuelven cada variante de ese problema, pero con semánticas distintas que vale la pena dominar para elegir la herramienta adecuada sin prueba y error.

Promise.all: éxito total o fallo inmediato

Promise.all espera a que todas las promesas del array se resuelvan. Si alguna rechaza, el conjunto rechaza de inmediato y el resto se ignora. Es la elección cuando necesitas todos los resultados o no puedes continuar.

const [usuario, pedidos, inventario] = await Promise.all([
  fetch('/api/usuario/42').then(r => r.json()),
  fetch('/api/pedidos?user=42').then(r => r.json()),
  fetch('/api/inventario').then(r => r.json()),
]);
// Si cualquiera falla, el await lanza un error

Las tres peticiones salen al mismo tiempo. En serie tardarían la suma de los tres tiempos; en paralelo con Promise.all tardas el tiempo de la más lenta.

Promise.allSettled: resultados completos sin importar errores

Promise.allSettled espera a que todas terminen, ya sea con éxito o con error, y devuelve un array de objetos con status ("fulfilled" o "rejected") y value o reason. Es útil para informes donde necesitas saber qué falló pero no quieres abortar todo.

const resultados = await Promise.allSettled([
  sincronizarModulo('pagos'),
  sincronizarModulo('envios'),
  sincronizarModulo('stock'),
]);

const errores = resultados
  .filter(r => r.status === 'rejected')
  .map(r => r.reason.message);

if (errores.length) {
  console.warn('Módulos con error:', errores);
}

Promise.race: el primero que termine gana

Promise.race resuelve (o rechaza) en cuanto la primera promesa del array se resuelve o rechaza. El patrón más útil es implementar un timeout controlado sin depender de librerías externas:

function conTimeout(promesa, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error(`Timeout tras ${ms}ms`)), ms)
  );
  return Promise.race([promesa, timeout]);
}

try {
  const datos = await conTimeout(fetch('/api/pesado').then(r => r.json()), 3000);
  console.log(datos);
} catch (err) {
  console.error(err.message); // "Timeout tras 3000ms" o error de red
}

Promise.any: el primero que tenga éxito

Promise.any (ES2021) resuelve con la primera promesa que tenga éxito, ignorando los rechazos intermedios. Solo rechaza si todas fallan, con un AggregateError. Es ideal para redundancia: intentar varios endpoints y quedarte con el más rápido disponible.

const cdn = await Promise.any([
  fetch('https://cdn1.ejemplo.com/asset.js').then(r => r.text()),
  fetch('https://cdn2.ejemplo.com/asset.js').then(r => r.text()),
  fetch('https://cdn3.ejemplo.com/asset.js').then(r => r.text()),
]);
// Obtiene el recurso del CDN más rápido que responda sin error

Limitar la concurrencia

Lanzar cien peticiones simultáneas puede saturar el servidor o el navegador. Una función simple de pool de concurrencia resuelve el problema procesando en lotes:

async function poolConcurrencia(items, limite, fn) {
  const resultados = [];
  for (let i = 0; i < items.length; i += limite) {
    const lote = items.slice(i, i + limite);
    const parcial = await Promise.all(lote.map(fn));
    resultados.push(...parcial);
  }
  return resultados;
}

// Procesar 50 IDs de 5 en 5
const ids = Array.from({ length: 50 }, (_, i) => i + 1);
const datos = await poolConcurrencia(ids, 5, id =>
  fetch(`/api/item/${id}`).then(r => r.json())
);

Cuándo usar cada método

Como regla general: usa Promise.all cuando necesites todos los resultados y un fallo debe abortar; Promise.allSettled cuando quieras resultados completos aunque haya errores parciales; Promise.race para timeouts o carreras de velocidad; y Promise.any para redundancia con varios proveedores. Conocer los cuatro te ahorra escribir lógica de combinación manual que acaba siendo más difícil de mantener.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP