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:
- Sintaxis : la API de
XMLHttpRequest
requería múltiples líneas de código para configurar las solicitudes. - Callbacks anidados: con este técnica las funciones anidadas eran difíciles de leer y depurar.
- Promesas: no soportaba promesas nativas, lo que hacía el manejo de errores y las operaciones asíncronas más difíciles.
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:
urls
: es una constante con un array que contiene la lista de URLs sobre las cuales vamos a hacer las solicitudes.Promise.all()
: toma el array de promesas (en este caso, una serie defetch
) y espera a que todas se resuelvan.await
: asegura que esperemos hasta que todas las solicitudes se completen antes de continuar.- Manejo de errores: con el bloque
try-catch
, capturamos cualquier error que ocurra durante las solicitudes.
¿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:
- Si una de las promesas falla,
Promise.all()
rechaza y no devolverá ninguna de las respuestas. Esto puede ser problemático si queremos procesar las respuestas que sí se resolvieron correctamente.
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:
- Hacer múltiples solicitudes de manera simultánea puede sobrecargar el servidor o llegar a los límites de las APIs. En esos casos, se recomienda implementar un mecanismo de limitación, como usar
Promise.allSettled()
o manejar las solicitudes en lotes.
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.
Más sobre Diseño y desarrollo Web
Deja una respuesta