// js · Web Platform Advent #2
Le Promise in JavaScript: una guida pratica
Comprendi le promise in JavaScript — crearle, then/catch/finally, il concatenamento e combinarne molte con Promise.all, race, any e allSettled — con esempi eseguibili e le insidie più comuni.
Una promise è un oggetto che rappresenta un valore non ancora pronto. È il fondamento del JavaScript asincrono: fetch(), i timer, le letture di file in Node e la maggior parte delle API moderne restituiscono promise. Una promise si trova sempre in uno di tre stati — in sospeso, mantenuta o rifiutata — e una volta risolta non cambia mai più.
Creare una promise
Il più delle volte consumi le promise che un'API ti fornisce. Ma puoi crearne una tu stesso con il costruttore Promise, che accetta una funzione executor che riceve resolve e reject:
const wait = (ms) => new Promise((resolve) => {
setTimeout(resolve, ms);
});
const fetchUser = (id) => new Promise((resolve, reject) => {
if (!id) reject(new Error('id is required'));
else resolve({ id, name: 'Ada' });
}); Chiamare resolve(value) mantiene la promise con quel valore; chiamare reject(error) la rifiuta. Anche qualsiasi cosa lanciata in modo sincrono dentro l'executor rifiuta la promise.
then, catch e finally
Leggi l'esito di una promise con .then() per il successo, .catch() per il fallimento e .finally() per la pulizia che viene eseguita in entrambi i casi:
fetchUser(42)
.then((user) => console.log('Got', user.name))
.catch((err) => console.error('Failed:', err.message))
.finally(() => console.log('Done')); .then() può accettare due argomenti — onFulfilled e onRejected — ma un .catch() separato alla fine della catena è più chiaro e cattura anche gli errori lanciati all'interno dei gestori .then() precedenti.
Concatenamento: il vero superpotere
Ogni .then() restituisce una nuova promise, quindi si concatenano. Qualunque cosa restituisci da un gestore diventa l'input del successivo. Cosa fondamentale: se restituisci una promise, la catena attende che si risolva prima di proseguire:
fetch('/api/user')
.then((res) => res.json()) // returns a promise → chain waits
.then((user) => fetch('/api/posts?user=' + user.id))
.then((res) => res.json())
.then((posts) => console.log(posts.length, 'posts'))
.catch((err) => console.error(err)); Un singolo .catch() alla fine gestisce un rifiuto proveniente da qualsiasi passaggio sopra di esso, ed è per questo che il concatenamento batte l'annidamento delle callback.
Eseguire più promise insieme
Quando le attività non dipendono l'una dall'altra, avviale in parallelo e combina i risultati. Quattro metodi statici coprono i casi più comuni:
Promise.all([...])— attende che tutte siano mantenute; viene rifiutata non appena una viene rifiutata. Restituisce un array di risultati in ordine.Promise.allSettled([...])— attende che tutte terminino e non viene mai rifiutata; restituisce un array di oggetti{ status, value }o{ status, reason }.Promise.race([...])— si risolve con la prima promise che si risolve, sia che venga mantenuta sia che venga rifiutata.Promise.any([...])— si risolve con la prima promise che viene mantenuta; viene rifiutata solo se tutte vengono rifiutate.
// Fetch three resources at once, fail if any fails
const [user, posts, prefs] = await Promise.all([
fetch('/api/user').then((r) => r.json()),
fetch('/api/posts').then((r) => r.json()),
fetch('/api/prefs').then((r) => r.json()),
]);
// Same, but tolerate individual failures
const results = await Promise.allSettled([taskA(), taskB(), taskC()]);
results.forEach((r) => {
if (r.status === 'fulfilled') console.log('ok', r.value);
else console.warn('failed', r.reason);
}); Insidie comuni
- Dimenticare il return. Dentro un
.then(), se chiami una promise ma non la restituisci conreturn, la catena non la attenderà — perdi l'ordinamento e la gestione degli errori. - Rifiuti non gestiti. Una promise senza
.catch()(o untry/catchattorno aawait) produce ununhandledrejection. Gestisci sempre gli errori alla fine della catena. - Mescolare callback e promise. Non chiamare
resolvedue volte né entrambiresolveereject— conta solo la prima chiamata; le altre vengono ignorate silenziosamente. - L'executor viene eseguito subito. La funzione che passi a
new Promise()viene eseguita in modo sincrono, immediatamente — solo le callback di.then()vengono rinviate.
Riferimento rapido
| Obiettivo | Metodo |
|---|---|
| Gestire il successo | .then(onFulfilled) |
| Gestire il fallimento | .catch(onRejected) |
| Pulizia in entrambi i casi | .finally(fn) |
| Tutte devono riuscire | Promise.all |
| Raccogliere ogni esito | Promise.allSettled |
| La prima a risolversi | Promise.race |
| La prima a riuscire | Promise.any |
Le promise sono l'impianto idraulico sotto async/await, che è solo una sintassi più gradevole sugli stessi oggetti. Una volta che il concatenamento e i quattro combinatori risultano naturali, il JavaScript asincrono smette di intimorire.