CAP 06 · LEC 04·Asincronía

Promise combinators: operaciones en paralelo

Cuando necesitas coordinar múltiples Promises, JavaScript ofrece cuatro combinadores: all, allSettled, race y any. Elegir el correcto puede significar la diferencia entre un error silencioso y una app robusta.

● INTERMEDIO8 min lectura4 ejerciciospor Fernando Herrera · actualizado mayo de 2026
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →

Promise.all — todas o ninguna

Promise.all espera a que todas las Promises resuelvan. Si una sola falla, el conjunto entero falla:

function cargarUsuario(id) { return new Promise(resolve => setTimeout(() => resolve({ id, nombre: `Usuario ${id}` }), 100) ); } function cargarConfiguracion() { return new Promise(resolve => setTimeout(() => resolve({ tema: "oscuro", idioma: "es" }), 150) ); } function cargarPermisos(userId) { return new Promise(resolve => setTimeout(() => resolve(["leer", "escribir"]), 80) ); } // ✅ Ejecutan en PARALELO — el total es ~150ms, no 330ms async function inicializarApp(userId) { const [usuario, config, permisos] = await Promise.all([ cargarUsuario(userId), cargarConfiguracion(), cargarPermisos(userId), ]); console.log(usuario.nombre); // Usuario 1 console.log(config.tema); // oscuro console.log(permisos); // ["leer", "escribir"] } inicializarApp(1); // ❌ Si una falla, Promise.all rechaza con ese error Promise.all([ Promise.resolve("ok"), Promise.reject(new Error("Falló el servidor")), Promise.resolve("también ok"), ]).catch(err => console.error(err.message)); // Falló el servidor
type Usuario = { id: number; nombre: string }; type Configuracion = { tema: string; idioma: string }; type Permiso = "leer" | "escribir" | "admin"; function cargarUsuario(id: number): Promise<Usuario> { return new Promise(resolve => setTimeout(() => resolve({ id, nombre: `Usuario ${id}` }), 100) ); } function cargarConfiguracion(): Promise<Configuracion> { return new Promise(resolve => setTimeout(() => resolve({ tema: "oscuro", idioma: "es" }), 150) ); } function cargarPermisos(userId: number): Promise<Permiso[]> { return new Promise(resolve => setTimeout(() => resolve(["leer", "escribir"]), 80) ); } async function inicializarApp(userId: number): Promise<void> { const [usuario, config, permisos]: [Usuario, Configuracion, Permiso[]] = await Promise.all([ cargarUsuario(userId), cargarConfiguracion(), cargarPermisos(userId), ]); console.log(usuario.nombre); // Usuario 1 console.log(config.tema); // oscuro console.log(permisos); // ["leer", "escribir"] } inicializarApp(1);
SalidaUsuario 1 oscuro ["leer", "escribir"]
Promise.all es fail-fast

Si cualquier Promise falla, Promise.all rechaza inmediatamente — sin esperar las otras. Las Promises restantes siguen ejecutándose en el motor, pero sus resultados se descartan. Usa allSettled si necesitas los resultados de todas.

Promise.allSettled — reporta cada resultado

A diferencia de all, allSettled siempre espera a que todas terminen y reporta el estado de cada una:

function cargarServicio(nombre, debefallar) { return new Promise((resolve, reject) => { setTimeout(() => { if (debefallar) { reject(new Error(`${nombre} no disponible`)); } else { resolve({ nombre, datos: "ok" }); } }, 100); }); } async function verificarServicios() { const resultados = await Promise.allSettled([ cargarServicio("Pagos", false), cargarServicio("Notificaciones", true), // este falla cargarServicio("Inventario", false), ]); resultados.forEach(resultado => { if (resultado.status === "fulfilled") { console.log("✓", resultado.value.nombre); } else { console.log("✗", resultado.reason.message); } }); } verificarServicios(); // ✓ Pagos // ✗ Notificaciones no disponible // ✓ Inventario
type Servicio = { nombre: string; datos: string }; function cargarServicio(nombre: string, debeFallar: boolean): Promise<Servicio> { return new Promise((resolve, reject) => { setTimeout(() => { if (debeFallar) { reject(new Error(`${nombre} no disponible`)); } else { resolve({ nombre, datos: "ok" }); } }, 100); }); } async function verificarServicios(): Promise<void> { const resultados = await Promise.allSettled([ cargarServicio("Pagos", false), cargarServicio("Notificaciones", true), cargarServicio("Inventario", false), ]); resultados.forEach((resultado: PromiseSettledResult<Servicio>) => { if (resultado.status === "fulfilled") { console.log("✓", resultado.value.nombre); } else { console.log("✗", (resultado.reason as Error).message); } }); } verificarServicios(); // ✓ Pagos // ✗ Notificaciones no disponible // ✓ Inventario
Salida✓ Pagos ✗ Notificaciones no disponible ✓ Inventario

Promise.race — gana la más rápida

Promise.race resuelve o rechaza con la primera Promise que termine — sea éxito o error:

function consultarServidor(servidor, latencia) { return new Promise(resolve => setTimeout(() => resolve(`Respuesta de ${servidor}`), latencia) ); } function timeout(ms) { return new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout: excedió ${ms}ms`)), ms) ); } // Caso 1: encontrar el servidor más rápido async function obtenerRespuestaRapida() { const respuesta = await Promise.race([ consultarServidor("EU", 300), consultarServidor("US", 150), // este gana consultarServidor("AS", 400), ]); console.log(respuesta); // Respuesta de US } // Caso 2: implementar un timeout para cualquier operación async function conTimeout(promesa, ms) { return Promise.race([promesa, timeout(ms)]); } async function probarTimeout() { try { const resultado = await conTimeout( consultarServidor("Lento", 1000), 500 ); console.log(resultado); } catch (err) { console.error(err.message); // Timeout: excedió 500ms } } obtenerRespuestaRapida(); probarTimeout();
function consultarServidor(servidor: string, latencia: number): Promise<string> { return new Promise(resolve => setTimeout(() => resolve(`Respuesta de ${servidor}`), latencia) ); } function timeout(ms: number): Promise<never> { return new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout: excedió ${ms}ms`)), ms) ); } async function obtenerRespuestaRapida(): Promise<void> { const respuesta = await Promise.race([ consultarServidor("EU", 300), consultarServidor("US", 150), consultarServidor("AS", 400), ]); console.log(respuesta); // Respuesta de US } async function conTimeout<T>(promesa: Promise<T>, ms: number): Promise<T> { return Promise.race([promesa, timeout(ms)]); } async function probarTimeout(): Promise<void> { try { const resultado = await conTimeout( consultarServidor("Lento", 1000), 500 ); console.log(resultado); } catch (err) { if (err instanceof Error) { console.error(err.message); // Timeout: excedió 500ms } } } obtenerRespuestaRapida(); probarTimeout();
SalidaRespuesta de US Timeout: excedió 500ms

Promise.any — el primero en tener éxito

Promise.any ignora rechazos y resuelve con la primera Promise que tenga éxito. Solo falla si todas fallan:

function intentarConServidor(url, latencia, funciona) { return new Promise((resolve, reject) => { setTimeout(() => { if (funciona) { resolve(`Conectado a: ${url}`); } else { reject(new Error(`${url} no responde`)); } }, latencia); }); } // Promise.any — ignora los rechazos, espera al primero en tener éxito async function conectarAlMejorServidor() { try { const conexion = await Promise.any([ intentarConServidor("cdn-1.ejemplo.com", 400, false), // falla intentarConServidor("cdn-2.ejemplo.com", 200, true), // resuelve ← gana intentarConServidor("cdn-3.ejemplo.com", 100, false), // falla ]); console.log(conexion); // Conectado a: cdn-2.ejemplo.com } catch (err) { // AggregateError — todos fallaron console.error("Sin servidores disponibles:", err.message); } } // Si TODOS fallan → AggregateError async function todosfallan() { try { await Promise.any([ Promise.reject(new Error("Servidor 1 caído")), Promise.reject(new Error("Servidor 2 caído")), ]); } catch (err) { console.error("Tipo:", err.constructor.name); // AggregateError console.error("Errores:", err.errors.length); // 2 } } conectarAlMejorServidor(); todosfallan();
function intentarConServidor( url: string, latencia: number, funciona: boolean ): Promise<string> { return new Promise((resolve, reject) => { setTimeout(() => { if (funciona) { resolve(`Conectado a: ${url}`); } else { reject(new Error(`${url} no responde`)); } }, latencia); }); } async function conectarAlMejorServidor(): Promise<void> { try { const conexion = await Promise.any([ intentarConServidor("cdn-1.ejemplo.com", 400, false), intentarConServidor("cdn-2.ejemplo.com", 200, true), intentarConServidor("cdn-3.ejemplo.com", 100, false), ]); console.log(conexion); // Conectado a: cdn-2.ejemplo.com } catch (err) { if (err instanceof AggregateError) { console.error("Sin servidores disponibles, errores:", err.errors.length); } } } async function todosFallan(): Promise<void> { try { await Promise.any([ Promise.reject(new Error("Servidor 1 caído")), Promise.reject(new Error("Servidor 2 caído")), ]); } catch (err) { if (err instanceof AggregateError) { console.error("Tipo:", err.constructor.name); // AggregateError console.error("Errores:", err.errors.length); // 2 } } } conectarAlMejorServidor(); todosFallan();
SalidaConectado a: cdn-2.ejemplo.com Tipo: AggregateError Errores: 2
CombinadorResuelve cuandoRechaza cuando
Promise.allTODAS resuelvenUNA falla (fail-fast)
Promise.allSettledTODAS terminan (sin importar resultado)Nunca rechaza — reporta cada estado
Promise.raceLa PRIMERA en terminar (éxito o error)La primera en terminar si es un rechazo
Promise.anyLa PRIMERA en resolver (solo éxito)TODAS rechazan (AggregateError)

Practica