Promise.any(): Primera Promesa Exitosa en JavaScript
Aprende a usar Promise.any() para obtener el resultado de la primera promesa que se resuelva exitosamente, ignorando los rechazos. Ideal para escenarios donde tienes múltiples fuentes de datos y solo necesitas una que funcione.
TL;DR - Resumen rápido
- Promise.any() devuelve una promesa que se resuelve con el valor de la primera promesa que se resuelva exitosamente
- Ignora todas las promesas que se rechazan, esperando solo la primera resolución exitosa
- Si todas las promesas se rechazan, devuelve un AggregateError con todos los errores
- Es ideal para escenarios con múltiples fuentes de datos redundantes donde solo necesitas una respuesta válida
- A diferencia de Promise.race(), Promise.any() solo se resuelve cuando hay AL MENOS UNA promesa exitosa
Introducción a Promise.any()
Promise.any() es un método estático del objeto Promise introducido en ES2021 que permite ejecutar múltiples promesas en paralelo y obtener el resultado de la primera que se resuelva exitosamente. A diferencia de Promise.race(), que se completa con la primera promesa que termina (ya sea resuelta o rechazada), Promise.any() ignora los rechazos y espera la primera resolución exitosa.
Este método es particularmente útil cuando tienes múltiples fuentes de datos redundantes (como diferentes servidores, APIs o caches) y solo necesitas una respuesta válida. Si una fuente falla, Promise.any() espera a la siguiente que tenga éxito, lo que hace tus aplicaciones más resilientes y tolerantes a fallos.
Diferencia clave con Promise.race()
Promise.race() se completa con la primera promesa que termine (resuelva O rechace), mientras que Promise.any() espera la primera promesa que se RESUELVA exitosamente, ignorando los rechazos. Esto hace que Promise.any() sea ideal para escenarios donde tienes múltiples intentos y solo necesitas uno exitoso.
Sintaxis Básica
La sintaxis de Promise.any() es similar a otros métodos de Promise. Recibe un iterable (generalmente un array) de promesas y devuelve una nueva promesa que se resuelve con el valor de la primera promesa que se resuelva exitosamente.
En este ejemplo, la primera promesa se rechaza después de 500ms, pero Promise.any() la ignora y espera la siguiente promesa que se resuelva exitosamente. La segunda promesa se resuelve después de 1000ms, por lo que Promise.any() se resuelve con su valor "Éxito 2". Aunque la tercera promesa también se resolvería, no es necesaria porque ya tenemos una resolución exitosa.
Promesas ya resueltas
Si pasas promesas que ya están resueltas a Promise.any(), la promesa resultante se resolverá inmediatamente con el valor de la primera promesa resuelta en el iterable. Esto es útil cuando quieres combinar promesas que pueden estar en diferentes estados.
Cómo Funciona Promise.any()
Promise.any() sigue un comportamiento determinista basado en el estado de las promesas que recibe. Entender este comportamiento es crucial para usarlo correctamente y evitar bugs sutiles en tu código.
- <strong>Primera en resolverse:</strong> Si la primera promesa en resolverse tiene éxito, Promise.any() se resuelve con ese valor.
- <strong>Rechazos ignorados:</strong> Las promesas que se rechazan se ignoran completamente, esperando la siguiente resolución exitosa.
- <strong>Todas rechazadas:</strong> Si TODAS las promesas se rechazan, Promise.any() se rechaza con un AggregateError.
- <strong>Valores no-promesa:</strong> Los valores que no son promesas se tratan como promesas ya resueltas.
- <strong>Iterable vacío:</strong> Si el iterable está vacío, Promise.any() se rechaza con un AggregateError.
Diferencias con Promise.race()
Es fundamental entender las diferencias entre Promise.any() y Promise.race() para elegir el método correcto según tu caso de uso.
Este ejemplo muestra claramente la diferencia: Promise.race() se rechaza inmediatamente porque la primera promesa se rechaza (0ms), mientras que Promise.any() ignora ese rechazo y espera la primera promesa que se resuelva exitosamente (1000ms). Promise.any() es "optimista" - espera el éxito, mientras que Promise.race() es "realista" - acepta cualquier resultado primero.
Cuándo usar cada uno
Usa Promise.race() cuando necesitas el resultado más rápido, incluso si es un error (útil para timeouts). Usa Promise.any() cuando tienes múltiples intentos y solo necesitas uno exitoso (útil para fuentes redundantes).
Casos de Uso Prácticos
Promise.any() tiene aplicaciones prácticas específicas donde brilla realmente en aplicaciones del mundo real, especialmente en escenarios de redundancia y tolerancia a fallos.
Fuentes de Datos Redundantes
Cuando tienes múltiples servidores o APIs que proporcionan el mismo dato, puedes usar Promise.any() para obtener la respuesta del servidor más rápido que responda exitosamente.
En este ejemplo, intentamos obtener datos de tres servidores diferentes. El servidor principal falla (rechaza), pero Promise.any() lo ignora y espera el siguiente servidor que responda exitosamente. El servidor de backup responde exitosamente en 1200ms, por lo que obtenemos sus datos. Es un patrón común en aplicaciones que requieren alta disponibilidad.
Manejo de AggregateError
Cuando todas las promesas se rechazan, Promise.any() se rechaza con un AggregateError que contiene todos los errores individuales. Esto te permite diagnosticar qué falló en cada intento.
Aquí, las tres promesas se rechazan con diferentes errores. Promise.any() se rechaza con un AggregateError que contiene un array de todos los errores en la propiedad `errors`. Esto es útil para debugging y para mostrar al usuario información detallada sobre qué falló.
Errores Comunes
Al trabajar con Promise.any(), hay varios errores que los desarrolladores cometen frecuentemente. Conocer estos errores te ayudará a evitarlos y escribir código más robusto.
- <strong>No manejar AggregateError:</strong> Si todas las promesas se rechazan, obtendrás un AggregateError. No manejarlo puede causar errores no manejados.
- <strong>Confundir con Promise.race():</strong> Promise.any() ignora rechazos, Promise.race() no. Usar el incorrecto puede causar comportamiento inesperado.
- <strong>Ignorar promesas pendientes:</strong> Las promesas que pierden la carrera siguen ejecutándose en segundo plano, lo que puede causar fugas de memoria.
- <strong>No validar el iterable:</strong> Pasar un iterable vacío causará un rechazo inmediato con AggregateError.
- <strong>No cancelar operaciones perdidas:</strong> Las promesas que pierden pueden seguir consumiendo recursos si no las cancelas explícitamente.
Advertencia: Promesas no cancelables
Las promesas nativas de JavaScript NO se pueden cancelar. Una vez que creas una promesa, se ejecutará hasta completarse. Si usas Promise.any() con peticiones HTTP, las peticiones que fallan siguen ejecutándose. Para cancelarlas, necesitas usar AbortController con fetch() o implementar tu propio mecanismo de cancelación.
Resumen: Promise.any()
Conceptos principales:
- •Promise.any() devuelve una promesa que se resuelve con el valor de la primera promesa que se resuelva exitosamente
- •Ignora todas las promesas que se rechazan, esperando solo la primera resolución exitosa
- •Si todas las promesas se rechazan, se rechaza con un AggregateError que contiene todos los errores
- •A diferencia de Promise.race(), Promise.any() solo se resuelve cuando hay AL MENOS UNA promesa exitosa
- •Es ideal para escenarios con múltiples fuentes de datos redundantes donde solo necesitas una respuesta válida
- •Los valores no-promesa se tratan como promesas ya resueltas
Mejores prácticas:
- •Usa Promise.any() cuando tengas múltiples fuentes redundantes y solo necesites una respuesta exitosa
- •Siempre maneja el AggregateError cuando todas las promesas pueden fallar
- •Combínalo con AbortController para cancelar peticiones HTTP que fallan
- •Usa Promise.race() si necesitas el resultado más rápido, incluso si es un error
- •Considera Promise.allSettled() si necesitas saber el resultado de todas las promesas
- •Encapsula patrones comunes en funciones reutilizables para mayor claridad