JavaScript è a thread singolo, eppure gestisce migliaia di operazioni concorrenti: chiamate API, letture di file, timer, eventi utente. Ci riesce grazie alla programmazione asincrona. Comprendere le Promise e async/await è un requisito imprescindibile per qualsiasi sviluppatore JavaScript.
L'Evoluzione: Callback → Promise → Async/Await
L'Era dei Callback
Callback Hell
// I callback annidati diventano rapidamente illeggibili
getUser(userId, (err, user) => {
if (err) return handleError(err);
getOrders(user.id, (err, orders) => {
if (err) return handleError(err);
getOrderDetails(orders[0].id, (err, details) => {
if (err) return handleError(err);
console.log(details); // Finalmente!
});
});
});
Le Promise: Un Approccio Migliore
Catena di Promise
getUser(userId) .then(user => getOrders(user.id)) .then(orders => getOrderDetails(orders[0].id)) .then(details => console.log(details)) .catch(err => handleError(err)) .finally(() => hideSpinner());
Async/Await: Promise in Forma Leggibile
Async/Await
async function getFullOrder(userId) {
try {
const user = await getUser(userId);
const orders = await getOrders(user.id);
const details = await getOrderDetails(orders[0].id);
return details;
} catch (err) {
handleError(err);
} finally {
hideSpinner();
}
}
Creare Promise
Costruttore di Promise
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function fetchWithTimeout(url, ms) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error("Timeout")), ms);
fetch(url)
.then(response => {
clearTimeout(timer);
resolve(response);
})
.catch(err => {
clearTimeout(timer);
reject(err);
});
});
}
Operazioni Concorrenti
Promise.all — Esecuzione in Parallelo
// Tutte devono riuscire (fallisce subito al primo rifiuto)
const [users, products, orders] = await Promise.all([
fetch("/api/users").then(r => r.json()),
fetch("/api/products").then(r => r.json()),
fetch("/api/orders").then(r => r.json()),
]);
// allSettled — Non viene mai rifiutata, restituisce lo stato di ciascuna
const results = await Promise.allSettled([
fetch("/api/users"),
fetch("/api/products"), // anche se questa fallisce...
fetch("/api/orders"), // ...otteniamo comunque i risultati delle altre
]);
results.forEach(result => {
if (result.status === "fulfilled") {
console.log("Successo:", result.value);
} else {
console.log("Fallita:", result.reason);
}
});
Promise.race — Vince la Prima che Termina
// Utile per i timeout
const result = await Promise.race([
fetch("/api/data"),
delay(5000).then(() => { throw new Error("Timeout!"); })
]);
Pattern di Gestione degli Errori
Funzione Wrapper (stile Go)
// Restituisce una tupla [errore, dati] — niente try/catch necessario
async function to(promise) {
try {
const data = await promise;
return [null, data];
} catch (err) {
return [err, null];
}
}
// Utilizzo
const [err, user] = await to(getUser(123));
if (err) {
console.error("Impossibile ottenere l'utente:", err.message);
return;
}
console.log(user);
Errori Comuni
- Dimenticare
await: Senzaawaitottieni un oggetto Promise, non il suo valore risolto. - Sequenziale quando intendevi parallelo:
await a(); await b();viene eseguito in sequenza. UsaPromise.all([a(), b()])per l'esecuzione in parallelo. - Rifiuti non gestiti: Aggiungi sempre un
.catch()alle catene di Promise oppure usa try/catch con async/await. - Async nei cicli:
forEachnon attende. Usafor...ofoppurePromise.all(arr.map(...)). - Creare Promise superflue: Se una funzione restituisce già una Promise, non incapsularla in
new Promise().
Prova i Nostri Strumenti JavaScript Gratuiti
Minifica e formatta il tuo codice JavaScript all'istante.