Las promesas en JavaScript son una de las poderosas API que nos ayudan a realizar operaciones Async.
Promise.all lleva las operaciones asincrónicas al siguiente nivel, ya que le ayuda a agregar un grupo de promesas.
En otras palabras, puedo decir que te ayuda a realizar operaciones concurrentes (a veces de forma gratuita).
Prerrequisitos:
Tienes que saber qué es una promesa en JavaScript.
¿Qué es Promise.all?
Promise.all es en realidad una promesa que toma una serie de promesas como entrada (iterable). Luego se resuelve cuando todas las promesas se resuelven o cualquiera de ellas es rechazada.
Por ejemplo, suponga que tiene diez promesas (operación asincrónica para realizar una llamada de red o una conexión de base de datos). Tienes que saber cuándo se resuelven todas las promesas o tienes que esperar hasta que se resuelvan todas las promesas. Así que le está pasando las diez promesas a Promise.all. Luego, Promise.todo como una promesa se resolverá una vez que las diez promesas se resuelvan o cualquiera de las diez promesas se rechace con un error.
Veámoslo en código:
Promise.all([Promise1, Promise2, Promise3]) .then(result) => { console.log(result) }) .catch(error => console.log(`Error in promises ${error}`))
Como puede ver, estamos pasando una matriz a Promise.all. Y cuando se resuelven las tres promesas, Promise.all se resuelve y la salida se consuela.
Veamos un ejemplo:
// A simple promise that resolves after a given time const timeOut = (t) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(`Completed in ${t}`) }, t) }) } // Resolving a normal promise. timeOut(1000) .then(result => console.log(result)) // Completed in 1000 // Promise.all Promise.all([timeOut(1000), timeOut(2000)]) .then(result => console.log(result)) // ["Completed in 1000", "Completed in 2000"]
En el ejemplo anterior, Promise.all se resuelve después de 2000 ms y la salida se consuela como una matriz.
Una cosa interesante sobre Promise.all es que se mantiene el orden de las promesas. La primera promesa en la matriz se resolverá en el primer elemento de la matriz de salida, la segunda promesa será un segundo elemento en la matriz de salida y así sucesivamente.
Veamos otro ejemplo:
// A simple promise that resolves after a given time const timeOut = (t) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(`Completed in ${t}`) }, t) }) } const durations = [1000, 2000, 3000] const promises = [] durations.map((duration) => { // In the below line, two things happen. // 1. We are calling the async function (timeout()). So at this point the async function has started and enters the 'pending' state. // 2. We are pushing the pending promise to an array. promises.push(timeOut(duration)) }) console.log(promises) // [ Promise { "pending" }, Promise { "pending" }, Promise { "pending" } ] // We are passing an array of pending promises to Promise.all // Promise.all will wait till all the promises get resolves and then the same gets resolved. Promise.all(promises) .then(response => console.log(response)) // ["Completed in 1000", "Completed in 2000", "Completed in 3000"]
En el ejemplo anterior, está claro que Promise.all espera hasta que se resuelvan todas las promesas.
Veamos qué pasa si se rechaza alguna de las promesas.
// A simple promise that resolves after a given time const timeOut = (t) => { return new Promise((resolve, reject) => { setTimeout(() => { if (t === 2000) { reject(`Rejected in ${t}`) } else { resolve(`Completed in ${t}`) } }, t) }) } const durations = [1000, 2000, 3000] const promises = [] durations.map((duration) => { promises.push(timeOut(duration)) }) // We are passing an array of pending promises to Promise.all Promise.all(promises) .then(response => console.log(response)) // Promise.all cannot be resolved, as one of the promises passed got rejected. .catch(error => console.log(`Error in executing ${error}`)) // Promise.all throws an error.
Como puede ver, si una de las promesas falla, el resto de las promesas fallan. Entonces Promise.all es rechazado.
Para algunos casos de uso, no es necesario. Debe ejecutar todas las promesas incluso si algunas han fallado, o tal vez pueda manejar las promesas fallidas más adelante.
Veamos cómo manejar eso.
const durations = [1000, 2000, 3000] promises = durations.map((duration) => { return timeOut(duration).catch(e => e) // Handling the error for each promise. }) Promise.all(promises) .then(response => console.log(response)) // ["Completed in 1000", "Rejected in 2000", "Completed in 3000"] .catch(error => console.log(`Error in executing ${error}`)) view raw
Casos de uso de Promise.all
Suponga que debe realizar una gran cantidad de operaciones asincrónicas, como enviar correos electrónicos de marketing masivo a miles de usuarios.
El pseudocódigo simple sería:
for (let i=0;i<50000; i += 1) { sendMailForUser(user[i]) // Async operation to send a email }
El ejemplo anterior es sencillo. Pero no es muy eficaz. La pila se volverá demasiado pesada y, en un momento dado, JavaScript tendrá una gran cantidad de conexiones HTTP abiertas que pueden matar al servidor.
Un enfoque sencillo de rendimiento sería hacerlo por lotes. Tome los primeros 500 usuarios, active el correo y espere hasta que se cierren todas las conexiones HTTP. Y luego tome el siguiente lote para procesarlo y así sucesivamente.
Veamos un ejemplo:
// Async function to send mail to a list of users. const sendMailForUsers = async (users) => { const usersLength = users.length for (let i = 0; i
{ // The batch size is 100. We are processing in a set of 100 users. return triggerMailForUser(user) // Async function to send the mail. .catch(e => console.log(`Error in sending email for ${user} - ${e}`)) // Catch the error if something goes wrong. So that it won't block the loop. }) // requests will have 100 or less pending promises. // Promise.all will wait till all the promises got resolves and then take the next 100. await Promise.all(requests) .catch(e => console.log(`Error in sending email for the batch ${i} - ${e}`)) // Catch the error. } } sendMailForUsers(userLists)
Consideremos otro escenario: debe crear una API que obtenga información de varias API de terceros y agregue todas las respuestas de las API.
Promise.all es la forma perfecta de hacerlo. Veamos cómo.
// Function to fetch Github info of a user. const fetchGithubInfo = async (url) => { console.log(`Fetching ${url}`) const githubInfo = await axios(url) // API call to get user info from Github. return { name: githubInfo.data.name, bio: githubInfo.data.bio, repos: githubInfo.data.public_repos } } // Iterates all users and returns their Github info. const fetchUserInfo = async (names) => { const requests = names.map((name) => { const url = `//api.github.com/users/${name}` return fetchGithubInfo(url) // Async function that fetches the user info. .then((a) => { return a // Returns the user info. }) }) return Promise.all(requests) // Waiting for all the requests to get resolved. } fetchUserInfo(['sindresorhus', 'yyx990803', 'gaearon']) .then(a => console.log(JSON.stringify(a))) /* Output: [{ "name": "Sindre Sorhus", "bio": "Full-Time Open-Sourcerer ·· Maker ·· Into Swift and Node.js ", "repos": 996 }, { "name": "Evan You", "bio": "Creator of @vuejs, previously @meteor & @google", "repos": 151 }, { "name": "Dan Abramov", "bio": "Working on @reactjs. Co-author of Redux and Create React App. Building tools for humans.", "repos": 232 }] */
Para concluir, Promise.all es la mejor manera de agregar un grupo de promesas a una sola promesa. Esta es una de las formas de lograr la concurrencia en JavaScript.
Espero que les haya gustado este artículo. Si lo hizo, aplauda y compártalo.
Incluso si no lo hizo, está bien, puede hacerlo de todos modos: P