Introduzione
Quando si lavora con le Promise in JavaScript, il caso d’uso più comune non è solo attendere un risultato asincrono, ma anche trasformare i dati passo dopo passo. Ad esempio: recuperi una risposta da un’API, la converti in JSON, estrai un campo utile, lo normalizzi e poi lo mostri a schermo.
In queste catene di operazioni, il punto critico è la gestione degli errori. Un errore può nascere in qualsiasi passaggio: dalla richiesta di rete, dal parsing del JSON, da una validazione manuale o da una funzione di trasformazione che lancia un’eccezione. In questi casi, .catch() diventa lo strumento fondamentale per intercettare il problema in un solo punto, senza dover ripetere controlli ovunque.
In questo tutorial vediamo un sotto-argomento molto pratico: come usare .catch() per gestire gli errori in una catena di Promise che trasforma i dati. È un approccio utile quando vuoi scrivere codice pulito, leggibile e facile da manutenere.
Codice completo
function fetchProduct(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (typeof id !== "number") {
reject(new Error("L´ID prodotto deve essere un numero"));
return;
}
// Simuliamo una risposta grezza da API
resolve({
id,
name: " cuffie bluetooth ",
price: "49.90",
stock: "12"
});
}, 300);
});
}
function parseProduct(rawProduct) {
// Questa funzione può lanciare errori se i dati non sono validi
if (!rawProduct || typeof rawProduct !== "object") {
throw new Error("Prodotto non valido");
}
return {
id: rawProduct.id,
name: rawProduct.name.trim(),
price: Number(rawProduct.price),
stock: Number(rawProduct.stock)
};
}
function validateProduct(product) {
if (!product.name) {
throw new Error("Il nome del prodotto è obbligatorio");
}
if (Number.isNaN(product.price) || product.price <= 0) {
throw new Error("Il prezzo non è valido");
}
if (!Number.isInteger(product.stock) || product.stock < 0) {
throw new Error("La disponibilità non è valida");
}
return product;
}
function formatProduct(product) {
return `${product.name.toUpperCase()} - €${product.price.toFixed(2)} - stock: ${product.stock}`;
}
fetchProduct(10)
.then(parseProduct)
.then(validateProduct)
.then(formatProduct)
.then((message) => {
console.log("Prodotto pronto:", message);
})
.catch((error) => {
console.error("Errore nella catena:", error.message);
}); Spiegazione
Il codice sopra mostra un flusso molto realistico: una Promise iniziale recupera un prodotto, poi ogni .then() applica una trasformazione.
1. fetchProduct(id) simula una richiesta asincrona
Questa funzione restituisce una Promise. Dopo un piccolo ritardo, verifica se l’ID passato è valido. Se non lo è, chiama reject() con un oggetto Error. Se invece tutto va bene, risolve con un oggetto prodotto “grezzo”, simile a quello che potresti ricevere da un’API.
2. parseProduct(rawProduct) trasforma i dati
Qui non stiamo usando una Promise: la funzione è sincrona. Questo è importante, perché se una funzione lancia un errore dentro una catena di Promise, l’errore viene intercettato da .catch(). Nel nostro esempio, se il prodotto non è un oggetto valido, la funzione lancia un’eccezione con throw new Error(...).
3. validateProduct(product) applica regole di business
Questa fase controlla i dati trasformati: nome presente, prezzo numerico e positivo, stock intero e non negativo. Anche qui usiamo throw per segnalare problemi. È un approccio semplice e molto efficace: invece di restituire valori speciali o null, interrompiamo subito il flusso.
4. formatProduct(product) produce l’output finale
Una volta che i dati sono stati validati, li convertiamo in una stringa leggibile. Questo è un esempio di trasformazione finale, utile per logging, UI o messaggi di conferma.
5. .catch() intercetta tutto
Il vantaggio principale della catena è che un solo .catch() finale può gestire gli errori provenienti da qualsiasi punto della sequenza: dalla Promise iniziale, da una funzione sincrona che lancia eccezioni, oppure da una trasformazione fallita.
In pratica, JavaScript converte automaticamente un errore lanciato in un then() in una Promise rifiutata, che poi arriva al primo .catch() utile.
Best practice
- Usa sempre oggetti
Errorcon messaggi chiari: evita stringhe generiche comereject("errore"). Un oggettoErrorè più utile per debugging e logging. - Preferisci un solo
.catch()finale quando vuoi gestire errori di tutta la catena. È più leggibile e riduce la duplicazione. - Lascia che le funzioni sincrone lancino eccezioni se rappresentano errori reali di validazione o trasformazione. Non è necessario “incapsulare tutto” in altre Promise.
- Non nascondere gli errori: se nel
.catch()fai solo logging, valuta se anche ripropagare l’errore conthrowo con una nuova Promise rifiutata, soprattutto in librerie o layer riutilizzabili. - Separa trasformazione e side effect: mantieni i
.then()dedicati alla trasformazione dati e usa l’ultimo passaggio per il rendering o la persistenza. - Evita catene troppo lunghe con logica mista: se la sequenza cresce troppo, spezzala in funzioni nominate. Ogni step deve avere una responsabilità chiara.
Riepilogo
Gestire gli errori con .catch() non significa solo “mostrare un messaggio se qualcosa va storto”. In una catena di Promise orientata alla trasformazione dei dati, .catch() è il punto centrale che raccoglie errori provenienti da più livelli: rifiuti espliciti, eccezioni lanciate nelle funzioni sincrone e fallimenti di validazione.
Il modello più utile da ricordare è questo:
- la Promise iniziale recupera i dati;
- ogni
.then()trasforma o valida; .catch()intercetta tutto ciò che fallisce lungo il percorso.
Questo approccio rende il codice più lineare, più facile da testare e più semplice da mantenere rispetto a una gestione degli errori distribuita in ogni singolo passaggio.
Approfondisci con risorse ufficiali
- MDN Web Docs - Promise: documentazione completa sul comportamento delle Promise in JavaScript.
- MDN Web Docs - Promise.prototype.catch(): riferimento ufficiale per capire come funziona
.catch(). - MDN Web Docs - Error: utile per creare e gestire correttamente gli oggetti errore.
- ECMAScript Language Specification: specifica ufficiale del linguaggio per i dettagli più tecnici sul comportamento delle Promise.
