Promisiunile în JavaScript sunt una dintre API-urile puternice care ne ajută să facem operațiuni Async.

Promise.all duce operațiunile Async la următorul nivel nou, deoarece vă ajută să agregați un grup de promisiuni.

Cu alte cuvinte, pot spune că vă ajută să faceți operațiuni concurente (uneori gratuit).

Condiții preliminare:

Trebuie să știi ce este un Promisiune în JavaScript.

Ce este Promise.all?

Promise.all este de fapt o promisiune care ia ca intrare o serie de promisiuni (un iterabil). Apoi se rezolvă atunci când toate promisiunile sunt rezolvate sau oricare dintre ele este respinsă.

De exemplu, presupuneți că aveți zece promisiuni (operație Async pentru a efectua un apel de rețea sau o conexiune la bază de date). Trebuie să știi când toate promisiunile se rezolvă sau trebuie să aștepți până când toate promisiunile se vor rezolva. Așadar, transmiteți toate cele zece promisiuni către Promise.all. Apoi, Promise.all însuși ca o promisiune va fi rezolvată odată ce toate cele zece promisiuni vor fi rezolvate sau oricare dintre cele zece promisiuni va fi respinsă cu o eroare.

Să o vedem în cod:

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

După cum puteți vedea, transmitem o matrice către Promise.all. Și când toate cele trei promisiuni sunt rezolvate, Promise.all se rezolvă și rezultatul este consolat.

Să vedem un exemplu:

// A simple promise that resolves after a given time
const timeOut = 
  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"]

În exemplul de mai sus, Promise.all se rezolvă după 2000 ms, iar ieșirea este consolată ca o matrice.

Un lucru interesant despre Promise.all este că ordinea promisiunilor este menținută. Prima promisiune din tablou se va rezolva la primul element al tabloului de ieșire, a doua promisiune va fi un al doilea element din tabloul de ieșire și așa mai departe.

Să vedem un alt exemplu:

// A simple promise that resolves after a given time
const timeOut = 
  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"]

Din exemplul de mai sus, este clar că Promise.all așteaptă până când toate promisiunile se vor rezolva.

Să vedem ce se întâmplă dacă una dintre promisiuni este respinsă.

// A simple promise that resolves after a given time
const timeOut = 
  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.

După cum puteți vedea, dacă una dintre promisiuni eșuează, atunci toate celelalte promisiuni eșuează. Apoi Promise.all devine respins.

Pentru unele cazuri de utilizare, nu aveți nevoie de asta. Trebuie să executați toate promisiunile, chiar dacă unele au eșuat, sau poate puteți face față promisiunilor eșuate mai târziu.

Să vedem cum să rezolvăm asta.

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

Folosiți cazuri de Promise.all

Să presupunem că trebuie să efectuați un număr mare de operațiuni Async, cum ar fi trimiterea de e-mailuri de marketing în bloc către mii de utilizatori.

Pseudo codul simplu ar fi:

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

Exemplul de mai sus este simplu. Dar nu este foarte performant. Stiva va deveni prea grea și la un moment dat, JavaScript va avea un număr mare de conexiuni HTTP deschise, care ar putea ucide serverul.

O abordare simplă performantă ar fi să o faceți în loturi. Luați primii 500 de utilizatori, declanșați e-mailul și așteptați până când toate conexiunile HTTP sunt închise. Și apoi luați următorul lot pentru al procesa și așa mai departe.

Să vedem un exemplu:

// Async function to send mail to a list of users.
const sendMailForUsers = async (users) => {
  const usersLength = users.length
  
  for (let i = 0; i < usersLength; i += 100) { 
    const requests = users.slice(i, i + 100).map((user) => { // 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)

Să luăm în considerare un alt scenariu: trebuie să construiți un API care obține informații de la mai multe API-uri terță parte și cumulează toate răspunsurile de la API-uri.

Promise.all este modul perfect de a face asta. Să vedem cum.

// 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 = `https://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
}]
*/

În concluzie, Promise.all este cel mai bun mod de a agrega un grup de promisiuni la o singură promisiune. Aceasta este una dintre modalitățile de realizare a concurenței în JavaScript.

Sper că ți-a plăcut acest articol. Dacă ați făcut-o, vă rugăm să bateți și să o împărtășiți.

Chiar dacă nu ați făcut-o, este bine că o puteți face oricum: P