soluzioni di controllo del flusso in Node.js - usa async.waterfall?

4

Esaminando node.js ed evitando i callback di annidamento. Ho trovato Async.js, qualcuno può mostrarmi come posso riscrivere questo codice usando il metodo async.waterfall?

// db is a mongodb instance
db.open(function(err, client){
    client.createCollection("docs", function(err, col) {
         client.collection("docs", function(err, col) {
             for (var i = 0; i < 100; i++) {
                 col.insert({c:i}, function() {});
             }
             console.log('Insert done OK.');
         });
    });
});

Quali problemi ha questa ingenua soluzione con le funzioni interne, rispetto al metodo asincrono?

db.open(openAndInsert);

function openAndInsert(err, client) {
    var myCollection = "docs";

    client.createCollection(myCollection, openCollection);

    function openCollection(err, col) {
        client.collection(myCollection, insertIntoCollection);
    }

    function insertIntoCollection(err, col) {
        for (var idx = 0; idx < 100; idx += 1) {
            col.insert({c:idx}, function () {});
        }
        console.log('Insert done OK.');
    }
}

I tre livelli di annidamento nel codice originale sembrano un odore di codice, ma non sono sicuro che la mia casa sia stata laminata o che la soluzione asincrona sia universalmente migliore. Qualcuno preferirebbe lasciare le funzioni annidate così com'è? Se sì, quanto dovrebbe essere profonda la nidificazione prima di usare un qualche tipo di controllo di flusso?

    
posta Sean 21.06.2013 - 15:04
fonte

3 risposte

1

Hai fatto diverse domande, risponderò alle ultime due.

L'annidamento di callback tramite le funzioni anonime è l'opposto del riutilizzo del codice e di DRY. Rende il codice più difficile da leggere dividendo la logica in una funzione mettendo una callback esattamente al centro.

Di solito quando si tenta di utilizzare funzioni più generali da utilizzare per i callback, si incontrano problemi che cercano solo di capire cosa fare in situazioni diverse (come errori). Finisci per fare un sacco di chiamate di funzioni nidificate e codice spaghetti che cerca di tenere conto di tutto.

I callback ci sono. Questa è la natura di nodejs.

Qualcosa come asyncjs o qcnode ti permette di scrivere codice in modo lineare che è più comprensibile e debugabile.

Penso che dovresti avere dei piccoli callback di stub che gestiscono gli errori e decidere se puoi andare avanti con ciò che stai cercando di fare, altrimenti dovrebbero abortire. Ho usato qcnode (ed è antenati) per anni e penso che questa ultima versione sia semplice da usare e molto utile.

Informativa completa: sono l'editore e lo sviluppatore principale di qcnode .

    
risposta data 21.06.2013 - 16:53
fonte
0

Questo fa quello che mi serve per usare async.js:

db.open(function (err, client) {
    async.waterfall([
        function (callback) {
            client.createCollection("docs", callback);
        },
        function (collection, callback) {
            for (var i = 0; i < 100; i++) {
                collection.insert({c:i}, function() {});
            }
            callback(null, 'ok');
        }
    ],
    function (err, result) {
        console.log('result: ', result);
    });
});

Inserendo il codice asincrono all'interno della funzione anonima passata a db.open la variabile "client" era visibile alla prima funzione asincrona. Ho trovato questa idea da questa risposta SO

    
risposta data 21.06.2013 - 18:29
fonte
0

Suggerirei entrambe le funzioni separate insieme asincrono ... Inoltre, tieni presente che Function.prototype.bind è incredibilmente potente.

//open the connection first, because the client may need to be used for cleanup.
db.open(function(err, client){

    //run the tasks in series, passing the result of each to the next task
    async.waterfall([
        //bind collection method to run against "docs"
        //there is no need to create a wrapper function yourself for this
        client.collection.bind(client, "docs")

        //passes the resulting collection to addToDocs
        ,addToDocs 

    ], completed); //run completed function at the end

    //will add 100 docs into the database
    function addToDocs(docs /*collection*/, cb) {
        //list of items to be run (async/parallel)
        var todo = [];
        for (var i=0; i<100; i++) {
            //bind the insert method against a new object for i, so it can be run later
            todo.push(docs.insert.bind(col, {c:i}));
        }

        //do all the inserts, then run the original callback
        async.parallel(todo, cb);

        //alternatively, you could have used async.series(todo, cb) to run one at a time.
    }

    function completed(err, results) {
        //close client resources, return connection to pool etc

        //handle any errors
        if (err) {
            console.log(err);
        }

        //any other work to be done
    }
});

Mi piace questo schema, in cui hai la tua logica in alto e metodi di riferimento sotto ...

NOTA: solo le dichiarazioni di funzione saranno issate per funzionare correttamente, var foo = function(){...} no.

    
risposta data 25.07.2013 - 01:24
fonte

Leggi altre domande sui tag