Gestione della piramide di callback node.js

9

Ho appena iniziato a utilizzare il nodo e una cosa che ho notato rapidamente è la velocità con cui i callback possono accumularsi fino a un livello di indentazione stupido:

doStuff(arg1, arg2, function(err, result) {
    doMoreStuff(arg3, arg4, function(err, result) {
        doEvenMoreStuff(arg5, arg6, function(err, result) {
            omgHowDidIGetHere();
        });
    });
});

La guida di stile ufficiale dice di mettere ciascuna richiamata in una funzione separata, ma che sembra eccessivamente restrittiva sull'uso delle chiusure, e rendendo un singolo oggetto dichiarato nel livello superiore disponibile diversi livelli in basso, poiché l'oggetto deve essere passato attraverso tutti i callback intermedi.

Va bene usare l'ambito delle funzioni per aiutare qui? Metti tutte le funzioni di callback che necessitano dell'accesso a un oggetto ish globale all'interno di una funzione che dichiara tale oggetto, quindi entra in una chiusura?

function topLevelFunction(globalishObject, callback) {

    function doMoreStuffImpl(err, result) {
        doMoreStuff(arg5, arg6, function(err, result) {
            callback(null, globalishObject);
        });
    }

    doStuff(arg1, arg2, doMoreStuffImpl);
}

e così via per molti altri livelli ...

O ci sono framework etc per aiutare a ridurre i livelli di indentazione senza dichiarare una funzione con nome per ogni singolo callback? Come gestisci la piramide callback?

    
posta thecoop 23.10.2013 - 11:53
fonte

3 risposte

7

Promises provide a clean separation of concerns between asynchronous behavior and the interface so asynchronous functions can be called without callbacks, and callback interaction can be done on the generic promise interface.

Esistono molteplici implementazioni di "promesse":

Ad esempio puoi riscrivere questo callback annidato

http.get(url.parse("http://test.com/"), function(res) {
    console.log(res.statusCode);
    http.get(url.parse(res.headers["location"]), function(res) {
        console.log(res.statusCode);
    });
});

come

httpGet(url.parse("http://test.com/")).then(function (res) {
    console.log(res.statusCode);
    return httpGet(url.parse(res.headers["location"]));
}).then(function (res) {
    console.log(res.statusCode);
});

Invece di richiamare in callback a(b(c())) fai il chain di ".then" a().then(b()).then(c()) .

Un'introduzione qui: link

    
risposta data 24.10.2013 - 14:27
fonte
3

In alternativa alle promesse dovresti dare un'occhiata alla parola chiave yield in combinazione con le funzioni del generatore che saranno introdotte in EcmaScript 6. Entrambe sono disponibili oggi su Node.js 0.11.x crea, ma richiede che tu specifichi ulteriormente il flag --harmony durante l'esecuzione di Node.js:

$ node --harmony app.js

L'utilizzo di questi costrutti e di una libreria come co di TJ Holowaychuk ti consente di scrivere codice asincrono in uno stile che sembra come il codice sincrono, sebbene funzioni ancora in modo asincrono. Fondamentalmente, queste cose insieme implementano il supporto co-routine per Node.js.

Fondamentalmente, quello che devi fare è scrivere una funzione generatore per il codice che esegue cose asincrone, chiamare la roba asincrona, ma prefisso con la parola chiave yield . Quindi, alla fine il tuo codice assomiglia a:

var run = function * () {
  var data = yield doSomethingAsync();
  console.log(data);
};

Per eseguire questa funzione di generatore hai bisogno di una libreria come quella di cui sopra. La chiamata sarà quindi:

co(run);

Oppure, per metterlo in linea:

co(function * () {
  // ...
});

Nota che dalle funzioni del generatore puoi chiamare altre funzioni del generatore, tutto ciò che devi fare è precedere di nuovo con yield .

Per un'introduzione a questo argomento, cerca su Google termini come yield generators es6 async nodejs e dovresti trovare tonnellate di informazioni. Ci vuole un po 'per abituarsi, ma una volta capito, non vuoi tornare indietro.

Si noti che questo non solo fornisce una sintassi più gradevole per chiamare le funzioni, ma consente anche di usare le usuali logiche di flusso di controllo (sincrone), come for loops o try / catch . Non dovrai più occuparti di molte callback e tutte queste cose.

Buona fortuna e buon divertimento: -)!

    
risposta data 15.01.2014 - 15:10
fonte
0

Ora hai il pacchetto asyncawait , con una sintassi molto vicina a quello che dovrebbe essere il supporto nativo futuro di await & async nel nodo.

Fondamentalmente ti permette di scrivere il codice asincrono guardando in sincrono , riducendo LOC e amp; livelli di rientro drammatici, con il compromesso di una leggera perdita di prestazioni (i numeri dei proprietari dei pacchetti sono del 79% rispetto ai callback non elaborati), si spera ridurranno quando sarà disponibile il supporto nativo.

Ancora una buona scelta per uscire dal callback hell / pyramid of doom nightmare IMO, quando le prestazioni non sono la preoccupazione principale & dove lo stile di scrittura sincrono si adatta meglio alle esigenze del tuo progetto.

Esempio di base del pacchetto doc:

var foo = async (function() {
    var resultA = await (firstAsyncCall());
    var resultB = await (secondAsyncCallUsing(resultA));
    var resultC = await (thirdAsyncCallUsing(resultB));
    return doSomethingWith(resultC);
});
    
risposta data 29.09.2016 - 13:45
fonte

Leggi altre domande sui tag