Usar Promise.all / allSettled / race / any

Domina los combinadores de promesas para coordinar múltiples tareas asíncronas con criterio de producto: rapidez, tolerancia a errores o primer resultado útil.

Concurrencia en JavaScript no significa paralelismo real de CPU, sino coordinar varias operaciones asíncronas sin bloquear la UI.

Los combinadores (`all`, `allSettled`, `race`, `any`) son herramientas de orquestación: eliges uno según el contrato de negocio.

Si necesitas todo correcto, usa `all`; si quieres analizar resultados parciales, `allSettled`; si buscas el primer resultado, `race` o `any`.

El objetivo avanzado es tomar decisiones predecibles ante latencia, fallos parciales y estados de carga en frontend.

  • Antes del código, decide el comportamiento esperado cuando una tarea falla o tarda demasiado.
  • En apps reales lanzas varias peticiones: perfil, permisos, notificaciones, métricas. No todas tienen el mismo peso para pintar la pantalla.
  • La diferencia entre una UI robusta y una frágil suele estar en cómo combinas esas promesas y qué estado devuelves en cada escenario.
  • `all` es ideal para pantallas que dependen de varios datos críticos y no tienen sentido incompletas.
  • `Promise.all` conserva el orden de entrada en su resultado, incluso si las promesas terminan en otro orden.

Modelo mental: coordinar promesas con intención

Antes del código, decide el comportamiento esperado cuando una tarea falla o tarda demasiado.

En apps reales lanzas varias peticiones: perfil, permisos, notificaciones, métricas. No todas tienen el mismo peso para pintar la pantalla.

La diferencia entre una UI robusta y una frágil suele estar en cómo combinas esas promesas y qué estado devuelves en cada escenario.

Promise.all: cuando todo debe estar listo

`all` es ideal para pantallas que dependen de varios datos críticos y no tienen sentido incompletas.

`Promise.all` conserva el orden de entrada en su resultado, incluso si las promesas terminan en otro orden.

Si una falla, se rechaza el bloque completo. Eso simplifica lógica cuando prefieres abortar y mostrar error global.

  • Ventaja: flujo simple y rápido cuando todo es obligatorio.
  • Riesgo: un fallo puntual tumba el conjunto completo.
  • Buena práctica: envolver en `try/catch` y mapear a un error de UI claro.

Promise.allSettled: resiliencia con resultados parciales

Cuando una sección puede fallar sin bloquear las demás, `allSettled` suele ser mejor decisión de producto.

Recibes un array de objetos `{ status, value | reason }` para cada promesa. Así decides qué módulos renderizar y cuáles degradar.

Es especialmente útil en paneles administrativos: métricas secundarias pueden fallar sin impedir la carga principal.

Promise.race y Promise.any: ganar por velocidad

Ambos eligen el primero, pero sus reglas de error son distintas y eso cambia el comportamiento del sistema.

`race` termina con la primera promesa completada, sea éxito o error. Es útil para componer timeout con una tarea principal.

`any` devuelve el primer éxito y solo falla con `AggregateError` si todas fallan. Es ideal para mirrors o múltiples proveedores.

  • Usa `race` para límites de tiempo y cancelaciones lógicas.
  • Usa `any` para alta disponibilidad con varias fuentes equivalentes.
  • Si usas `any`, maneja explícitamente `AggregateError` para mostrar causa consolidada.

Matriz de decisión y anti-patrones

Elegir bien evita bugs de estados imposibles y mensajes de error inconsistentes.

Pregunta clave: ¿qué necesita el usuario para seguir avanzando? Si una parte es opcional, no la trates como bloqueante total.

Documenta el contrato de cada combinador en el servicio para que el equipo no rompa ese comportamiento al refactorizar.

  • `all`: datos obligatorios para habilitar la vista.
  • `allSettled`: tolerancia a fallos parciales y telemetría por módulo.
  • `race`: primer resultado (incluye error), útil para timeout técnico.
  • `any`: primer éxito, útil para redundancia de proveedores.

Código del tema: const tareas = [fetch('/api/a'), fetch('/api/b')];

📘 Teoría

Modelo mental: coordinar promesas con intención

Antes del código, decide el comportamiento esperado cuando una tarea falla o tarda demasiado.

En apps reales lanzas varias peticiones: perfil, permisos, notificaciones, métricas. No todas tienen el mismo peso para pintar la pantalla.

La diferencia entre una UI robusta y una frágil suele estar en cómo combinas esas promesas y qué estado devuelves en cada escenario.

1

Promise.all

Éxito global o fallo global (fail-fast). Ideal cuando todo es obligatorio para continuar.

2

Promise.allSettled

Siempre resuelve y te da el estado de cada promesa. Útil para dashboards con módulos independientes.

3

Promise.race

Gana la primera promesa que se complete (resuelta o rechazada). Útil para timeouts.

4

Promise.any

Devuelve la primera promesa resuelta con éxito; ignora rechazos hasta que todas fallen.

Promise.all: cuando todo debe estar listo

`all` es ideal para pantallas que dependen de varios datos críticos y no tienen sentido incompletas.

`Promise.all` conserva el orden de entrada en su resultado, incluso si las promesas terminan en otro orden.

Si una falla, se rechaza el bloque completo. Eso simplifica lógica cuando prefieres abortar y mostrar error global.

  • Ventaja: flujo simple y rápido cuando todo es obligatorio.
  • Riesgo: un fallo puntual tumba el conjunto completo.
  • Buena práctica: envolver en `try/catch` y mapear a un error de UI claro.
Carga conjunta de datos críticos
async function cargarVistaPrincipal(userId) {
  const [perfil, permisos, notificaciones] = await Promise.all([
    fetch(`/api/users/${userId}/profile`).then(r => r.json()),
    fetch(`/api/users/${userId}/permissions`).then(r => r.json()),
    fetch(`/api/users/${userId}/notifications`).then(r => r.json())
  ]);

  return { perfil, permisos, notificaciones };
}

Promise.allSettled: resiliencia con resultados parciales

Cuando una sección puede fallar sin bloquear las demás, `allSettled` suele ser mejor decisión de producto.

1

Recibes un array de objetos `{ status, value | reason }` para cada promesa. Así decides qué módulos renderizar y cuáles degradar.

2

Es especialmente útil en paneles administrativos: métricas secundarias pueden fallar sin impedir la carga principal.

Clasificar módulos disponibles y fallidos
Revisar
const tareas = [
  fetch('/api/kpis/ventas').then(r => r.json()),
  fetch('/api/kpis/soporte').then(r => r.json()),
  fetch('/api/kpis/marketing').then(r => r.json())
];

const resultados = await Promise.allSettled(tareas);

const ok = resultados
  .map((r, i) => ({ r, i }))
  .filter(({ r }) => r.status === 'fulfilled')
  .map(({ r, i }) => ({ modulo: i, data: r.value }));

const errores = resultados
  .map((r, i) => ({ r, i }))
  .filter(({ r }) => r.status === 'rejected')
  .map(({ r, i }) => ({ modulo: i, error: String(r.reason) }));

Promise.race y Promise.any: ganar por velocidad

Ambos eligen el primero, pero sus reglas de error son distintas y eso cambia el comportamiento del sistema.

`race` termina con la primera promesa completada, sea éxito o error. Es útil para componer timeout con una tarea principal.

`any` devuelve el primer éxito y solo falla con `AggregateError` si todas fallan. Es ideal para mirrors o múltiples proveedores.

  • Usa `race` para límites de tiempo y cancelaciones lógicas.
  • Usa `any` para alta disponibilidad con varias fuentes equivalentes.
  • Si usas `any`, maneja explícitamente `AggregateError` para mostrar causa consolidada.
Timeout con race + fallback con any
Revisar
function timeout(ms) {
  return new Promise((_, reject) => {
    setTimeout(() => reject(new Error('Tiempo excedido')), ms);
  });
}

const respuestaRapida = await Promise.race([
  fetch('/api/report').then(r => r.json()),
  timeout(2500)
]);

const mirrors = [
  fetch('https://mirror-a.example.com/data').then(r => r.json()),
  fetch('https://mirror-b.example.com/data').then(r => r.json()),
  fetch('https://mirror-c.example.com/data').then(r => r.json())
];

const primerDatoValido = await Promise.any(mirrors);

Matriz de decisión y anti-patrones

Elegir bien evita bugs de estados imposibles y mensajes de error inconsistentes.

Pregunta clave: ¿qué necesita el usuario para seguir avanzando? Si una parte es opcional, no la trates como bloqueante total.

Documenta el contrato de cada combinador en el servicio para que el equipo no rompa ese comportamiento al refactorizar.

  • `all`: datos obligatorios para habilitar la vista.
  • `allSettled`: tolerancia a fallos parciales y telemetría por módulo.
  • `race`: primer resultado (incluye error), útil para timeout técnico.
  • `any`: primer éxito, útil para redundancia de proveedores.

🧪 Aprende probando

Ejemplo Ejemplo guiado: paralelo real de operaciones asíncronas Compara la duración total cuando lanzas peticiones juntas con `Promise.all`.
Ejemplo Ejemplo guiado: clasificar éxitos y errores con allSettled Aprende a degradar funcionalidad sin romper la pantalla completa.
Ejemplo Demo interactiva: comparar all, allSettled, race y any Lanza simulaciones de red y observa cómo cambia el resultado según el combinador.

🏁 Retos

Reto Reto 1: cargar dashboard crítico con Promise.all Implementa una función que combine dos llamadas obligatorias y devuelva un único objeto listo para UI.
Reto Reto 2: primer proveedor disponible con Promise.any Resuelve con la primera respuesta exitosa entre tres proveedores y maneja el caso en que todos fallen.

🧰 Recursos

¿Qué es esto?

Soy Cristian Eslava y a veces hago webs para procrastinar yo y vosotros 😉.

Esta la hice en febrero de 2026 para facilitar el aprendizaje de mis alumnxs. Aprender desarrollo web practicando. La idea es que crezca semanalmente con nuevos temas, tests y retos.

Inspirado en MDN, en W3Schools, en Codepen, en el crack de Manz y en mil sitios de documentación sobre desarrollo web. Quería aportar además de bloques teóricos con ejemplos, la gamificación de los retos y el sistema de test que ya tenía en culTest .

Si te gustó, si no te gustó, si quieres saludarme, o invitarme a 🍻 no dudes en escribirme en cristianeslava@gmail.com .