Всичко, което трябва да знаете за Promise.all

Обещанията в JavaScript са един от мощните API, които ни помагат да извършваме Async операции.

Promise.all извежда операциите на Async на следващото ново ниво, тъй като ви помага да обедините група обещания.

С други думи, мога да кажа, че това ви помага да правите едновременни операции (понякога безплатно).

Предпоставки:

Трябва да знаете какво е Обещание в JavaScript.

Какво е Promise.all?

Promise.all всъщност е обещание, което приема масив от обещания като вход (итерируемо). След това се решава, когато всички обещания се разрешат или някое от тях бъде отхвърлено.

Например, приемете, че имате десет обещания (Async операция за извършване на мрежово повикване или връзка с база данни). Трябва да знаете кога всички обещания се разрешават или трябва да изчакате, докато всички обещания се разрешат. Така че предавате всички десет обещания на Promise.all. След това Promise.all като обещание ще бъде разрешено, след като всички десет обещания бъдат разрешени или някое от десетте обещания бъде отхвърлено с грешка.

Нека го видим в код:

Promise.all([Promise1, Promise2, Promise3]) .then(result) => { console.log(result) }) .catch(error => console.log(`Error in promises ${error}`))

Както можете да видите, ние предаваме масив на Promise.all. И когато и трите обещания се решат, Promise.all решава и изходът се утешава.

Да видим пример:

// 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"]

В горния пример Promise.all се разрешава след 2000 ms и изходът се консолира като масив.

Едно интересно нещо за Promise.all е, че редът на обещанията се поддържа. Първото обещание в масива ще бъде разрешено за първия елемент на изходния масив, второто обещание ще бъде втори елемент в изходния масив и т.н.

Нека да видим друг пример:

// 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"] 

От горния пример става ясно, че Promise.all чака, докато всички обещания се разрешат.

Нека да видим какво ще стане, ако някое от обещанията бъде отхвърлено.

// 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. 

Както можете да видите, ако едно от обещанията се провали, тогава всички останали обещания се провалят. Тогава Promise.all получава отказ.

За някои случаи на употреба това не ви е необходимо. Трябва да изпълните всички обещания, дори ако някои са се провалили, или може би можете да се справите с неуспешните обещания по-късно.

Нека да видим как да се справим с това.

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

Използвайте случаи на Promise.all

Да приемем, че трябва да извършите огромен брой асинхронни операции, като изпращане на групови маркетингови имейли до хиляди потребители.

Обикновеният псевдо код би бил:

for (let i=0;i<50000; i += 1) { sendMailForUser(user[i]) // Async operation to send a email }

Горният пример е ясен. Но това не е много ефективно. Стекът ще стане твърде тежък и в един момент от времето JavaScript ще има огромен брой отворена HTTP връзка, която може да убие сървъра.

Един прост ефективен подход би бил да се прави на партиди. Вземете първите 500 потребители, задействайте пощата и изчакайте, докато всички HTTP връзки бъдат затворени. След това вземете следващата партида, за да я обработите и така нататък.

Да видим пример:

// 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)

Нека разгледаме друг сценарий: Трябва да изградите API, който получава информация от множество API на трети страни и обединява всички отговори от API.

Promise.all е идеалният начин да направите това. Да видим как.

// 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 }] */ 

В заключение, Promise.all е най-добрият начин за обединяване на група обещания в едно обещание. Това е един от начините за постигане на паралелност в JavaScript.

Надявам се тази статия да ви е харесала. Ако сте го направили, моля, пляскайте и го споделете.

Дори и да не сте, това е добре, можете да го направите така или иначе: P