Όλα όσα πρέπει να γνωρίζετε για το 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

Ας υποθέσουμε ότι πρέπει να εκτελέσετε έναν τεράστιο αριθμό λειτουργιών Async, όπως η αποστολή μαζικών email μάρκετινγκ σε χιλιάδες χρήστες.

Ο απλός ψευδο κωδικός θα ήταν:

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