Async/Await: scrivere codice asincrono come se fosse sincrono
try/catch con async, esecuzione in parallelo con Promise.all, top level await
async e await sono zucchero sintattico costruito sopra le Promise: permettono di scrivere codice che aspetta un risultato asincrono con un aspetto quasi identico al codice sincrono, una riga dopo l'altra, senza catene di then(). Una funzione dichiarata async restituisce sempre una Promise; await, utilizzabile solo al suo interno, mette in pausa l'esecuzione di quella funzione finché la Promise indicata non si conclude.
await è come ordinare un caffè al bar e restare lì ad aspettarlo leggendo il giornale, invece di lasciare il numero al banco e farsi chiamare quando è pronto. Il locale, con tutti gli altri clienti, continua comunque a funzionare nel frattempo: sei soltanto tu, in quel momento, a esserti messo in pausa, non l'intero bar.
1. Da then() ad await: la stessa cosa, scritta diversamente
Le due versioni seguenti fanno esattamente la stessa cosa: recuperano un utente dal server e ne stampano il nome.
// con then()
function recuperaUtente(id) {
return fetch(`/api/utenti/${id}`)
.then((risposta) => risposta.json())
.then((utente) => {
console.log(utente.nome);
});
}
// con async/await
async function recuperaUtente(id) {
const risposta = await fetch(`/api/utenti/${id}`);
const utente = await risposta.json();
console.log(utente.nome);
}
2. Gestire gli errori con try/catch
Quando la Promise indicata da await viene rifiutata, l'errore viene sollevato in quel punto esatto del codice, esattamente come farebbe un throw: si intercetta quindi con un normale blocco try/catch, senza bisogno di un catch() agganciato alla Promise.
async function recuperaUtente(id) {
try {
const risposta = await fetch(`/api/utenti/${id}`);
const utente = await risposta.json();
console.log(utente.nome);
} catch (errore) {
console.error('Impossibile recuperare l\'utente:', errore.message);
}
}
3. La trappola comune: await in sequenza rallenta senza motivo
Un errore frequente è mettere in await, uno dopo l'altro, richieste che non dipendono tra loro: ognuna aspetta la fine della precedente prima di partire, anche se potrebbero benissimo correre insieme.
// lento: la seconda richiesta parte solo dopo che la prima è finita
const meteo = await fetch('/api/meteo').then((r) => r.json());
const traffico = await fetch('/api/traffico').then((r) => r.json());
Se le due richieste sono indipendenti, conviene farle partire insieme con Promise.all() e aspettarle entrambe in un colpo solo, come visto nella pillola precedente.
// più veloce: le due richieste partono insieme
const [meteo, traffico] = await Promise.all([
fetch('/api/meteo').then((r) => r.json()),
fetch('/api/traffico').then((r) => r.json()),
]);
4. Top level await: fuori da una funzione async
Nei moduli ES (i file caricati con <script type="module">, o con estensione .mjs in Node.js), è possibile usare await direttamente nello scope principale del modulo, senza racchiuderlo in una funzione async: è comodo, per esempio, per caricare una configurazione prima che il resto del modulo venga eseguito.
// dentro un modulo ES (type="module")
const configurazione = await fetch('/config.json').then((r) => r.json());
console.log(configurazione);
In sintesi
async e await non sostituiscono le Promise: le fanno solo leggere come codice sincrono, riga per riga, con gli errori gestibili con un normale try/catch. L'unica accortezza da ricordare è non mettere in sequenza, con più await uno dopo l'altro, richieste che potrebbero correre in parallelo: in quel caso Promise.all() resta lo strumento giusto. Nella prossima pillola della serie vedremo la Fetch API più da vicino: come costruire richieste HTTP native, senza librerie esterne.
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.