front-end javascript Intermediate 3 min

Callback e Callback Hell: il problema e le soluzioni

Error-first callback, pyramid of doom, perché diventano un problema

· 600 parole

Una callback è una funzione passata come argomento a un'altra funzione, per essere eseguita dopo: spesso dopo un'operazione asincrona come una lettura da file o una richiesta di rete. Da sola, un callback non è un problema: lo diventa quando più operazioni dipendenti si accumulano una dentro l'altra.

Una callback singola è come chiedere a qualcuno «quando hai finito, chiamami». Perfettamente ragionevole. Il problema nasce se, dentro quella chiamata, gli dici anche «e quando ti richiamo, tu chiama un altro, che a sua volta ne chiamerà un altro ancora»: una catena di favori annidati che, dopo il quarto anello, nessuno riesce più a seguire con chiarezza.

1. La convenzione error-first: l'errore va sempre per primo

Una convenzione diffusa (nata in Node.js, ma comune in tutto l'ecosistema JavaScript) vuole che un callback riceva l'errore come primo argomento, il risultato come secondo.

function leggiFile(percorso, callback) {
    // ... operazione asincrona ...
    callback(errore, contenuto);
}

leggiFile('dati.txt', function (errore, contenuto) {
    if (errore) {
        console.error('Qualcosa e\' andato storto:', errore);
        return;
    }
    console.log(contenuto);
});

Il motivo di questo ordine, non casuale: mettere l'errore per primo costringe chi scrive il callback a pensarci subito, invece di poter scrivere comodamente function(contenuto) {} e dimenticarsi della gestione errori.

Fonte: la documentazione ufficiale Node.js, sezione «Error-first callbacks», descrive questa convenzione come lo standard dell'ecosistema.

2. Il problema: la piramide che cresce verso destra

Quando più operazioni asincrone dipendono l'una dal risultato dell'altra, le callback si annidano, e il codice cresce verso destra a ogni passo, invece che verso il basso.

doSomething(function (result) {
    doSomethingElse(result, function (newResult) {
        doThirdThing(newResult, function (finalResult) {
            console.log('Risultato finale:', finalResult);
        }, gestisciErrore);
    }, gestisciErrore);
}, gestisciErrore);

Questa forma ha un nome informale ma diffusissimo: «callback hell» o «pyramid of doom», proprio per la forma triangolare che assume l'indentazione crescente.

Fonte: MDN Web Docs, «Using promises»: fare in sequenza diverse operazioni asincrone porta al classico «callback hell», con annidamento crescente e gestione degli errori ripetuta a ogni livello.

3. Perché è un problema reale, non solo estetico

Gestione errori ripetuta: ogni livello annidato richiede il proprio controllo d'errore, come si vede sopra, con gestisciErrore passato a ogni chiamata separatamente.

Difficile aggiungere uno step: inserire una nuova operazione nel mezzo della catena significa riscrivere l'indentazione di tutto ciò che segue.

Difficile seguire il flusso: il codice non si legge più «dall'alto in basso» in modo naturale, bisogna seguire mentalmente ogni livello di annidamento per capire l'ordine reale delle operazioni.

4. La soluzione: appiattire con le Promise

Le Promise risolvono proprio questo difetto fondamentale della piramide di callback, catturando tutti gli errori, comprese le eccezioni lanciate, in un unico punto centrale.

doSomething()
    .then((result) => doSomethingElse(result))
    .then((newResult) => doThirdThing(newResult))
    .then((finalResult) => {
        console.log('Risultato finale:', finalResult);
    })
    .catch((errore) => {
        console.error('Qualcosa e\' andato storto:', errore);
    });

Un solo .catch() alla fine gestisce l'errore, qualunque sia lo step in cui si è verificato: niente più ripetizione, niente più piramide. MDN raccomanda inoltre di mantenere le catene di Promise piatte, senza annidarle: l'annidamento nelle Promise è quasi sempre un segno di composizione poco curata, non una necessità.

Fonte: MDN Web Docs, «Using promises»: le Promise risolvono il problema fondamentale della piramide di callback catturando tutti gli errori in un unico punto; le catene di Promise vanno tenute piatte, l'annidamento è di solito il risultato di una composizione poco attenta.

In sintesi

I callback non sono «sbagliati»: restano il meccanismo di base su cui poggia tutto il resto, comprese le Promise stesse. Il problema nasce solo quando le dipendenze tra operazioni asincrone si accumulano senza uno strumento per appiattirle. Le Promise, e poi async/await costruito sopra di esse, sono quello strumento.

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