Damián De Luca - Capacitación & Desarrollo Web

Promise.all() en JavaScript

JavaScript
Promise.all() en JavaScript

Desde la incorporación de fetch en ECMAScript 6 (ES6), los desarrolladores web hemos tenido acceso a una herramienta más limpia y moderna para manejar solicitudes HTTP, reemplazando gradualmente a técnicas antiguas como XMLHttpRequest. Sin embargo, a medida que las aplicaciones web se han vuelto más complejas, la necesidad de manejar múltiples solicitudes simultáneamente ha llevado a soluciones. Entre las necesidades que necesitamos cubrir se encuentra la posibilidad de ejecutar varias promesas en paralelo. Para esto podemos emplear técnicas modernas de JavaScript, como Promise.all(), combinadas con async y await.

En este artículo analizaremos mecanismos que permiten la combinación de varias solicitudes fetch ejecutadas simultáneamente utilizando Promise.all(). Este enfoque nos permite realizar múltiples solicitudes HTTP de forma concurrente, esperando a que todas finalicen antes de continuar.

Incorporación de fetch y la evolución de JavaScript

El método fetch fue introducido en 2015 con la versión ES6. Su llegada marcó una diferencia significativa en cómo los desarrolladores manejaban las solicitudes HTTP. Antes de fetch, usábamos XMLHttpRequest, una API que requería una cantidad considerable de código boilerplate y era difícil de leer y mantener.

fetch no solo proporciona una sintaxis más limpia, sino que también devuelve promesas, lo que facilita el manejo de operaciones asíncronas. Con la incorporación de async/await en ES2017, la forma de escribir código asíncrono mejoró aún más, permitiendo una estructura más legible y sincrónica.

Limitaciones de técnicas anteriores

Con XMLHttpRequest teníamos que tener en cuenta las siguientes limitaciones y consideraciones a la hora de trabajar:

Aunque las promesas mejoraron el manejo asíncrono, las cadenas largas de promesas podían volverse difíciles de gestionar, especialmente cuando se necesitaban múltiples solicitudes concurrentes.

Ventajas de fetch con Promise.all()

Al usar Promise.all(), podemos manejar múltiples solicitudes al mismo tiempo y asegurarnos de que todas terminen antes de proceder con cualquier lógica adicional. En lugar de esperar secuencialmente, como ocurriría en una cadena de promesas o utilizando async/await de manera individual para cada llamada fetch, Promise.all() permite ejecutar todas las promesas en paralelo.

A continuación veremos un ejemplo que se basa en una constante de JavaScript con 3 URLs (usuarios, posts y comentarios). Usaremos una función asíncrona con manejo de errores y veremos la potencia del método Promise.all() en acción:

const urls = [
    'https://api.example.com/users',
    'https://api.example.com/posts',
    'https://api.example.com/comments'
];

async function fetchAllData() {
    try {
        const [users, posts, comments] = await Promise.all(urls.map(url => fetch(url).then(res => res.json())));
        console.log('Usuarios:', users);
        console.log('Publicaciones:', posts);
        console.log('Comentarios:', comments);
    } catch (error) {
        console.error('Error al obtener datos:', error);
    }
}

fetchAllData();

Explicación del código:

¿Por qué usar promise.all() con async y await?

A continuación veremos 3 claves que explican porque conviene usar promise.all() con async y await?

1. Legibilidad. El uso de async/await hace que el código asíncrono sea más fácil de leer y escribir, como si fuera código sincrónico.

2. Paralelismo. Promise.all() ejecuta todas las solicitudes en paralelo, lo que mejora el rendimiento cuando se necesitan múltiples datos de diferentes fuentes.

3. Manejo de errores eficiente. Este mecanismo nos facilita la posibilidad de capturar errores en todas las solicitudes simultáneamente usando un único bloque try-catch.

Limitaciones de Promise.all()

Aunque Promise.all() es extremadamente útil, también tiene algunas limitaciones que debemos tener en cuenta:

1. Error en una promesa:

   const urls = [
       'https://api.example.com/users',  // Supongamos que esta URL es inválida
       'https://api.example.com/posts'
   ];

   async function fetchAllData() {
       try {
           const [users, posts] = await Promise.all(urls.map(url => fetch(url).then(res => res.json())));
       } catch (error) {
           console.error('Error:', error); // Si una falla, ninguna se devuelve.
       }
   }

Solución: para evitar que el fallo de una promesa afecte a todas, podemos manejar las promesas individualmente usando un patrón como este:

   async function fetchAllData() {
       const results = await Promise.all(urls.map(url => 
           fetch(url).then(res => res.json()).catch(err => ({ error: err }))
       ));
       console.log(results); // Nos devuelve los datos de las exitosas y los errores de las fallidas.
   }

2. Sobrecarga de solicitudes:

Alternativas a Promise.all()

Promise.allSettled()fue introducido en ES2020, este método devuelve una promesa que se resuelve cuando todas las promesas se han completado, sin importar si fueron resueltas o rechazadas.

Esto puede resultar muy útil si deseamos manejar cada promesa por separado sin que el fallo de una interrumpa el resto. A continuación veremos un código de ejemplo:

   async function fetchAllData() {
       const results = await Promise.allSettled(urls.map(url => fetch(url).then(res => res.json())));
       console.log(results); // Devuelve tanto resultados como errores, pero no detiene el proceso.
   }

En conclusión

Por lo que hemos podido apreciar en este artículo las ventajas que aporta Promise.all()en JavaScript son numerosas para necesidades intermedias y avanzadas de desarrollo de sitios y aplicaciones Web.

El uso de Promise.all() en combinación con async/await es una técnica poderosa para realizar múltiples solicitudes de manera concurrente en JavaScript. Mejora significativamente la legibilidad del código y optimiza el rendimiento de las aplicaciones al ejecutar todas las solicitudes en paralelo. Sin embargo, también es importante tener en cuenta las limitaciones, como la posibilidad de fallos en una de las promesas y la sobrecarga del servidor, para las cuales podemos recurrir a otras herramientas como Promise.allSettled() o técnicas de manejo de errores avanzadas (para saber más sobre estas ténicases recomiendo el artículo de MDN).

El mundo del desarrollo web sigue evolucionando, y técnicas modernas como estas nos permiten crear aplicaciones más rápidas, eficientes y escalables. Conocer la evolución de JavaScript nos permite construir soluciones más robustas, código optimizado y más fácil de mantener.

Salir de la versión móvil