Obećanja u JavaScriptu jedan su od moćnih API-ja koji nam pomažu u izvršavanju Async operacija.
Promise.all podiže Async operacije na sljedeću novu razinu jer vam pomaže prikupljanju grupe obećanja.
Drugim riječima, mogu reći da vam pomaže u istodobnim operacijama (ponekad i besplatno).
Preduvjeti:
Morate znati što je obećanje u JavaScript-u.
Što je Promise.all?
Promise.all zapravo je obećanje koje uzima niz obećanja kao ulaz (iterable). Tada se to rješava kada se riješe sva obećanja ili neko od njih odbije.
Na primjer, pretpostavimo da imate deset obećanja (Async operacija za obavljanje mrežnog poziva ili veze s bazom podataka). Morate znati kada se sva obećanja razriješe ili morate pričekati dok se sva obećanja ne riješe. Dakle, svih deset obećanja prosljeđujete Promise.all-u. Zatim će se Promise.all sam kao obećanje razriješiti nakon što se riješi svih deset obećanja ili ako bilo koje od deset obećanja bude odbijeno s pogreškom.
Pogledajmo u kodu:
Promise.all([Promise1, Promise2, Promise3]) .then(result) => { console.log(result) }) .catch(error => console.log(`Error in promises ${error}`))
Kao što vidite, prenosimo niz na Promise.all. A kad se riješe sva tri obećanja, Promise.all riješi i izlaz se utješi.
Pogledajmo primjer:
// 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"]
U gornjem primjeru Promise.all rješava nakon 2000 ms i izlaz je utješen kao niz.
Jedna zanimljiva stvar u vezi s Promise.all je da se redoslijed obećanja održava. Prvo će se obećanje u polju razriješiti za prvi element izlaznog niza, drugo će obećanje biti drugi element u izlaznom nizu i tako dalje.
Pogledajmo još jedan primjer:
// 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"]
Iz gornjeg primjera jasno je da Promise.all čeka dok se sva obećanja ne riješe.
Pogledajmo što će se dogoditi ako se neko od obećanja odbije.
// 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.
Kao što vidite, ako jedno od obećanja propadne, onda sva ostala obećanja propadnu. Tada Promise.all dobiva odbijenicu.
Za neke slučajeve upotrebe to vam nije potrebno. Morate izvršiti sva obećanja, čak i ako neka nisu uspjela, ili možda kasnije možete riješiti neuspjela obećanja.
Pogledajmo kako to riješiti.
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
Koristite slučajeve Promise.all
Pretpostavimo da morate izvesti ogroman broj Async operacija poput slanja skupnih marketinških e-poruka tisućama korisnika.
Jednostavni pseudo kod bi bio:
for (let i=0;i<50000; i += 1) { sendMailForUser(user[i]) // Async operation to send a email }
Gornji je primjer izravno. Ali nije baš izvedbeno. Stog će postati pretežak i u jednom će trenutku JavaScript imati ogroman broj otvorenih HTTP veza što može ubiti poslužitelj.
Jednostavan izvedbeni pristup bio bi raditi u skupinama. Uzmite prvih 500 korisnika, pokrenite poštu i pričekajte dok se sve HTTP veze ne zatvore. A zatim uzmite sljedeću seriju da biste je obradili i tako dalje.
Pogledajmo primjer:
// 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)
Razmotrimo još jedan scenarij: Morate izgraditi API koji dobiva informacije od više API-ja treće strane i agregira sve odgovore iz API-ja.
Promise.all je savršen način za to. Da vidimo kako.
// 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 }] */
Da zaključimo, Promise.all je najbolji način za združivanje grupe obećanja u jedno obećanje. Ovo je jedan od načina postizanja istodobnosti u JavaScript-u.
Nadam se da vam se svidio ovaj članak. Ako jeste, pljeskajte i podijelite ga.
Čak i ako nisi, to je u redu, svejedno možeš to učiniti: P