front-end javascript Intermediate 4 min

Promise.all(), allSettled(), race() e any()

Concorrenza controllata, errori parziali e la corsa tra più Promise

· 709 parole

Spesso serve avviare più operazioni asincrone insieme, invece che una dopo l'altra in catena, e reagire quando sono tutte concluse, o quando lo è solo una parte, o quando lo è la prima. JavaScript mette a disposizione quattro metodi statici della classe Promise pensati esattamente per questi quattro scenari.

Immagina di ordinare la cena da quattro ristoranti diversi per una festa. Con Promise.all() vuoi che arrivino tutti e quattro i piatti, se anche un solo ristorante annulla l'ordine consideri saltata l'intera cena. Con Promise.allSettled() vuoi comunque sapere come è andata con ciascuno, e servi in tavola quello che è arrivato, ristorante per ristorante. Con Promise.race() hai fame subito e reagisci al primo evento in assoluto, che sia un piatto o una telefonata di cancellazione. Con Promise.any() aspetti invece il primo piatto buono che arriva, ignorando le cancellazioni, a meno che non abbiano cancellato proprio tutti.

Linea del tempo con tre richieste e i risultati di Promise.all, allSettled, race e any: rifiuto a 1s, tutti gli esiti a 3s, errore a 1s, valore a 2s

1. Promise.all(): tutte devono andare a buon fine

Promise.all() riceve un array di Promise e ne restituisce una nuova, che si conclude con successo solo quando tutte le Promise dell'array sono state mantenute, restituendo un array dei risultati nello stesso ordine di partenza. Se anche una sola viene rifiutata, la Promise combinata si rifiuta subito con quello stesso motivo, senza aspettare le altre.

const richiestaMeteo = fetch('/api/meteo').then((r) => r.json());
const richiestaTraffico = fetch('/api/traffico').then((r) => r.json());
const richiestaEventi = fetch('/api/eventi').then((r) => r.json());

Promise.all([richiestaMeteo, richiestaTraffico, richiestaEventi])
    .then(([meteo, traffico, eventi]) => {
        console.log('Tutti i dati sono arrivati:', meteo, traffico, eventi);
    })
    .catch((errore) => {
        console.error('Almeno una richiesta è fallita:', errore.message);
    });

Fonte: la pagina di riferimento MDN «Promise.all()» descrive il comportamento con array vuoti, valori non Promise e rifiuti.

2. Promise.allSettled(): il risultato di ognuna, comunque vada

Promise.allSettled() non si rifiuta mai: aspetta che tutte le Promise si siano concluse, in un modo o nell'altro, e restituisce sempre un array di oggetti, uno per ciascuna Promise originale, con un campo status pari a 'fulfilled' oppure 'rejected'.

Promise.allSettled([richiestaMeteo, richiestaTraffico, richiestaEventi])
    .then((risultati) => {
        risultati.forEach((risultato, indice) => {
            if (risultato.status === 'fulfilled') {
                console.log(`Richiesta ${indice} riuscita:`, risultato.value);
            } else {
                console.warn(`Richiesta ${indice} fallita:`, risultato.reason);
            }
        });
    });

Fonte: la pagina di riferimento MDN «Promise.allSettled()» documenta la forma degli oggetti restituiti per ogni Promise.

3. Promise.race(): vince la prima che si conclude

Promise.race() si conclude non appena la prima Promise dell'array raggiunge uno stato finale, sia esso successo o fallimento, ignorando tutte le altre. È il metodo giusto per implementare un timeout su un'operazione asincrona: basta far correre l'operazione contro un timer.

function conTimeout(promessa, millisecondi) {
    const timeout = new Promise((resolve, reject) => {
        setTimeout(() => reject(new Error('Richiesta troppo lenta')), millisecondi);
    });

    return Promise.race([promessa, timeout]);
}

conTimeout(fetch('/api/dati'), 3000)
    .then((risposta) => console.log('Arrivata in tempo'))
    .catch((errore) => console.error(errore.message));

Fonte: la pagina di riferimento MDN «Promise.race()» spiega il comportamento con la prima Promise che si conclude, in successo o in fallimento.

4. Promise.any(): la prima che va a buon fine

Promise.any() assomiglia a race(), ma ignora i rifiuti lungo la strada: si conclude con successo non appena una qualsiasi delle Promise viene mantenuta. Si rifiuta solo se tutte le Promise dell'array falliscono, restituendo un AggregateError che raccoglie tutti i singoli motivi di fallimento.

Promise.any([richiestaMeteo, richiestaTraffico, richiestaEventi])
    .then((primoRisultato) => {
        console.log('Il primo dato disponibile e\' arrivato:', primoRisultato);
    })
    .catch((erroreAggregato) => {
        console.error('Tutte le richieste sono fallite:', erroreAggregato.errors);
    });

Fonte: la pagina di riferimento MDN «Promise.any()» documenta il comportamento e l'oggetto AggregateError restituito quando tutte le Promise falliscono.

In sintesi

I quattro metodi si distinguono per una sola cosa: cosa considerano un successo e cosa un fallimento dell'operazione combinata. Promise.all() pretende il successo di tutte, fallendo subito appena una sola fallisce. Promise.allSettled() aspetta comunque tutte, ma non fallisce mai: restituisce il risultato di ciascuna. Promise.race() risponde alla velocità della prima Promise che si conclude, qualunque sia l'esito. Promise.any() aspetta invece la prima che ha successo, ignorando quelle fallite nel frattempo. Nella prossima pillola della serie vedremo async e await: la sintassi che rende il codice asincrono leggibile come se fosse sincrono.

Accedi o registrati per votare, salvare o commentare la pillola

Commenti (0)

Registrati per commentare, fare domande e proporre l'argomento della prossima pillola (oppure accedi se hai già un account).

Nessun commento. Sii il primo a scrivere qualcosa.

Pillole correlate