Comment éviter la longue imbrication des fonctions asynchrones dans le noeud.js

je veux faire une page qui affiche des données à partir d'une base, j'ai donc créé certaines fonctions que d'obtenir que les données de ma DB. Je suis juste un débutant dans le Nœud.js, donc autant que je comprends, si je veux les utiliser toutes en une seule page (réponse HTTP) j'aurais du nid:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  getSomeDate(client, function(someData) {
    html += "<p>"+ someData +"</p>";
    getSomeOtherDate(client, function(someOtherData) {
      html += "<p>"+ someOtherData +"</p>";
      getMoreData(client, function(moreData) {
        html += "<p>"+ moreData +"</p>";
        res.write(html);
        res.end();
      });
    });
  });

S'il y a beaucoup de fonctions comme cela, alors la nidification devient un problème .

y a-t-il un moyen d'éviter cela? Je suppose qu'il a pour faire avec la façon dont vous combinez de multiples fonctions asynchrones, qui semble être quelque chose de fondamental.

149
demandé sur Kay Pale 2010-11-20 22:43:45

23 réponses

observation intéressante. Notez que dans JavaScript, vous pouvez normalement remplacer les fonctions de rappel anonymes inline par des variables de fonction nommées.

le suivant:

http.createServer(function (req, res) {
   // inline callback function ...

   getSomeData(client, function (someData) {
      // another inline callback function ...

      getMoreData(client, function(moreData) {
         // one more inline callback function ...
      });
   });

   // etc ...
});

pourrait être réécrit pour ressembler à quelque chose comme ceci:

var moreDataParser = function (moreData) {
   // date parsing logic
};

var someDataParser = function (someData) {
   // some data parsing logic

   getMoreData(client, moreDataParser);
};

var createServerCallback = function (req, res) {
   // create server logic

   getSomeData(client, someDataParser);

   // etc ...
};

http.createServer(createServerCallback);

cependant, sauf si vous prévoyez de réutiliser la logique de rappel dans d'autres endroits, il est souvent beaucoup plus facile de lire les fonctions anonymes en ligne, comme dans votre exemple. Il épargnera également d'avoir à trouver un nom pour tous les rappels.

en outre noter que comme @pst noté dans un commentaire ci-dessous, si vous accédez à des variables de fermeture dans les fonctions internes, ce qui précède ne serait pas une traduction simple. Dans de tels cas, l'utilisation de fonctions anonymes en ligne est encore plus préférable.

71
répondu Daniel Vassallo 2017-05-23 12:34:42

Kay, il suffit d'utiliser l'un de ces modules.

Ce sera à son tour:

dbGet('userIdOf:bobvance', function(userId) {
    dbSet('user:' + userId + ':email', 'bobvance@potato.egg', function() {
        dbSet('user:' + userId + ':firstName', 'Bob', function() {
            dbSet('user:' + userId + ':lastName', 'Vance', function() {
                okWeAreDone();
            });
        });
    });
});

dans ceci:

flow.exec(
    function() {
        dbGet('userIdOf:bobvance', this);

    },function(userId) {
        dbSet('user:' + userId + ':email', 'bobvance@potato.egg', this.MULTI());
        dbSet('user:' + userId + ':firstName', 'Bob', this.MULTI());
        dbSet('user:' + userId + ':lastName', 'Vance', this.MULTI());

    },function() {
        okWeAreDone()
    }
);
62
répondu Baggz 2010-11-27 00:07:57

pour la plupart, je suis D'accord avec Daniel Vassallo. Si vous pouvez décomposer une fonction compliquée et profondément imbriquée en fonctions nommées séparées, alors c'est généralement une bonne idée. Pour les moments où il est logique de le faire à l'intérieur d'une seule fonction, vous pouvez utiliser l'un des nombreux nœud.js async bibliothèques disponibles. Les gens sont venus avec beaucoup de différentes façons de s'attaquer à cela, alors jetez un oeil au noeud.js modules page et voir ce que vous en pensez.

j'ai écrit un module pour cela moi-même, appelé async.js . En utilisant ceci, l'exemple ci-dessus pourrait être mis à jour comme:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  async.series({
    someData: async.apply(getSomeDate, client),
    someOtherData: async.apply(getSomeOtherDate, client),
    moreData: async.apply(getMoreData, client)
  },
  function (err, results) {
    var html = "<h1>Demo page</h1>";
    html += "<p>" + results.someData + "</p>";
    html += "<p>" + results.someOtherData + "</p>";
    html += "<p>" + results.moreData + "</p>";
    res.write(html);
    res.end();
  });
});

une bonne chose à propos de cette approche est que vous pouvez rapidement changer votre code pour récupérer les données en parallèle en changeant la fonction "série" en "parallèle". Qui plus est, async.js va aussi le travail à l'intérieur du navigateur, de sorte que vous pouvez utiliser les mêmes méthodes que dans le nœud.js si vous rencontrez un code async délicat.

J'espère que c'est utile!

18
répondu Caolan 2010-11-21 16:11:41

vous pouvez utiliser cette astuce avec un tableau plutôt que des fonctions imbriquées ou un module.

beaucoup plus facile pour les yeux.

var fs = require("fs");
var chain = [
    function() { 
        console.log("step1");
        fs.stat("f1.js",chain.shift());
    },
    function(err, stats) {
        console.log("step2");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step3");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step4");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step5");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("done");
    },
];
chain.shift()();

vous pouvez étendre l'idiome pour les processus parallèles ou même chaînes parallèles de processus:

var fs = require("fs");
var fork1 = 2, fork2 = 2, chain = [
    function() { 
        console.log("step1");
        fs.stat("f1.js",chain.shift());
    },
    function(err, stats) {
        console.log("step2");
        var next = chain.shift();
        fs.stat("f2a.js",next);
        fs.stat("f2b.js",next);
    },
    function(err, stats) {
        if ( --fork1 )
            return;
        console.log("step3");
        var next = chain.shift();

        var chain1 = [
            function() { 
                console.log("step4aa");
                fs.stat("f1.js",chain1.shift());
            },
            function(err, stats) { 
                console.log("step4ab");
                fs.stat("f1ab.js",next);
            },
        ];
        chain1.shift()();

        var chain2 = [
            function() { 
                console.log("step4ba");
                fs.stat("f1.js",chain2.shift());
            },
            function(err, stats) { 
                console.log("step4bb");
                fs.stat("f1ab.js",next);
            },
        ];
        chain2.shift()();
    },
    function(err, stats) {
        if ( --fork2 )
            return;
        console.log("done");
    },
];
chain.shift()();
18
répondu Guido 2012-12-03 02:06:58

j'aime async.js beaucoup à cet effet.

Le problème est résolu par la chute d'eau de la commande:

chute d'eau(tâches, [rappel])

exécute un tableau de fonctions en série, chacune passant leurs résultats à la suivante dans le tableau. Cependant, si l'une des fonctions transmet une erreur au callback, la fonction suivante n'est pas exécutée et le callback principal est immédiatement appelé avec le erreur.

Arguments

tâches-un ensemble de fonctions à exécuter, chaque fonction est passée un callback(err, result1, result2, ...) il doit faire appel à son achèvement. Le premier argument est une erreur (qui peut être nulle) et les autres arguments seront passés comme arguments pour la tâche suivante. callback (err, [results]) - un callback optionnel à exécuter une fois toutes les fonctions terminées. Ce sera transmis les résultats de la dernière tâche de rappel.

exemple

async.waterfall([
    function(callback){
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback){
        callback(null, 'three');
    },
    function(arg1, callback){
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
    // result now equals 'done'    
});

comme pour les variables req, res,elles seront partagées à l'intérieur de la même portée que la fonction(req, res){} qui incluait l'async entier.cascade d'appel.

non seulement ainsi, async est très propre. Ce que je veux dire, c'est que je change beaucoup de cas comme celui-ci:

function(o,cb){
    function2(o,function(err, resp){
        cb(err,resp);
    })
}

À la première:

function(o,cb){
    function2(o,cb);
}

puis à ceci:

function2(o,cb);

puis à ceci:

async.waterfall([function2,function3,function4],optionalcb)

il permet également de nombreuses fonctions de premade préparé pour async d'être appelé à partir d'util.js très rapide. Enchaînez juste ce que vous voulez faire, assurez-vous o,cb est universellement manipulé. Cela accélère considérablement le processus de codage.

14
répondu Grant Li 2013-11-18 17:55:03

vous avez besoin d'un peu de sucre syntaxique. Chek:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = ["<h1>Demo page</h1>"];
  var pushHTML = html.push.bind(html);

  Queue.push( getSomeData.partial(client, pushHTML) );
  Queue.push( getSomeOtherData.partial(client, pushHTML) );
  Queue.push( getMoreData.partial(client, pushHTML) );
  Queue.push( function() {
    res.write(html.join(''));
    res.end();
  });
  Queue.execute();
}); 

Jolie soigné , n'est-ce pas? Vous pouvez remarquer que html est devenu un tableau. Cela s'explique en partie par le fait que les chaînes de caractères sont immuables, de sorte que vous feriez mieux de tamponner votre sortie dans un tableau plutôt que de rejeter des chaînes de plus en plus grandes. L'autre raison est due à une autre jolie syntaxe avec bind .

Queue dans la exemple est vraiment juste un exemple, et avec partial peut être mis en œuvre comme suit:

// Functional programming for the rescue
Function.prototype.partial = function() {
  var fun = this,
      preArgs = Array.prototype.slice.call(arguments);
  return function() {
    fun.apply(null, preArgs.concat.apply(preArgs, arguments));
  };
};

Queue = [];
Queue.execute = function () {
  if (Queue.length) {
    Queue.shift()(Queue.execute);
  }
};
11
répondu galambalazs 2010-11-22 20:38:53

je Suis amoureux Async.js depuis que je l'ai trouvé. Il a une fonction async.series que vous pouvez utiliser pour éviter la nidification longue.

de la Documentation:-


de la série(tâches, [rappel])

exécute un tableau de fonctions en série, chacune tournant une fois la fonction précédente terminée. [...]

Arguments

tasks - un tableau de fonctions à exécuter, chaque fonction est passée un rappel qu'elle doit appeler à l'achèvement. callback(err, [results]) - un rappel optionnel à exécuter une fois toutes les fonctions terminées. Cette fonction reçoit un tableau de tous les arguments passés aux callbacks utilisés dans le tableau.


Voici comment nous pouvons l'appliquer à votre code d'exemple:-

http.createServer(function (req, res) {

    res.writeHead(200, {'Content-Type': 'text/html'});

    var html = "<h1>Demo page</h1>";

    async.series([
        function (callback) {
            getSomeData(client, function (someData) { 
                html += "<p>"+ someData +"</p>";

                callback();
            });
        },

        function (callback) {
            getSomeOtherData(client, function (someOtherData) { 
                html += "<p>"+ someOtherData +"</p>";

                callback(); 
            });
        },

        funciton (callback) {
            getMoreData(client, function (moreData) {
                html += "<p>"+ moreData +"</p>";

                callback();
            });
        }
    ], function () {
        res.write(html);
        res.end();
    });
});
7
répondu Salman Abbas 2013-02-12 01:07:30

le sucre syntaxique le plus simple que j'ai vu est un noeud-promesse.

npm install node-promise | / git clone https://github.com/kriszyp/node-promise

en utilisant ceci vous pouvez enchaîner les méthodes asynchrones comme:

firstMethod().then(secondMethod).then(thirdMethod);

la valeur de retour de chacun est disponible comme argument dans le suivant.

6
répondu Nikhil Ranjan 2016-04-18 02:40:40

ce que vous avez fait là est de prendre un motif asynch et de l'appliquer à 3 fonctions appelées dans l'ordre, chacun d'eux attendant que le précédent à compléter avant de commencer - i.e. vous les avez fait synchrone . Le point à propos de la programmation asynch est que vous pouvez avoir plusieurs fonctions toutes en cours d'exécution à la fois et ne pas avoir à attendre que chacun complète.

si getSomeDate () ne fournit rien pour getSomeOtherDate(), qui ne fournit pas n'importe quoi pour getMoreData() alors pourquoi ne pas les appeler asynchrones comme js le permet ou s'ils sont interdépendants (et pas asynchrones) les écrire comme une seule fonction?

vous n'avez pas besoin d'utiliser l'imbrication pour contrôler le flux - par exemple, obtenir chaque fonction pour terminer en appelant une fonction commune qui détermine quand tous les 3 ont terminé et envoie ensuite la réponse.

3
répondu Nick Tulett 2010-11-20 22:20:50

supposons que vous puissiez faire ceci:

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    var html = "<h1>Demo page</h1>";
    chain([
        function (next) {
            getSomeDate(client, next);
        },
        function (next, someData) {
            html += "<p>"+ someData +"</p>";
            getSomeOtherDate(client, next);
        },
        function (next, someOtherData) {
            html += "<p>"+ someOtherData +"</p>";
            getMoreData(client, next);
        },
        function (next, moreData) {
            html += "<p>"+ moreData +"</p>";
            res.write(html);
            res.end();
        }
    ]);
});

il suffit d'implémenter chain () pour qu'il applique partiellement chaque fonction à la suivante, et n'invoque immédiatement que la première fonction:

function chain(fs) {
    var f = function () {};
    for (var i = fs.length - 1; i >= 0; i--) {
        f = fs[i].partial(f);
    }
    f();
}
2
répondu ngn 2010-11-22 05:58:35

l'enfer de rappel peut être facilement évité en javascript pur avec fermeture. la solution ci-dessous suppose que toutes les callbacks suivent la signature function(error, data).

http.createServer(function (req, res) {
  var modeNext, onNext;

  // closure variable to keep track of next-callback-state
  modeNext = 0;

  // next-callback-handler
  onNext = function (error, data) {
    if (error) {
      modeNext = Infinity;
    } else {
      modeNext += 1;
    }
    switch (modeNext) {

    case 0:
      res.writeHead(200, {'Content-Type': 'text/html'});
      var html = "<h1>Demo page</h1>";
      getSomeDate(client, onNext);
      break;

    // handle someData
    case 1:
        html += "<p>"+ data +"</p>";
        getSomeOtherDate(client, onNext);
        break;

    // handle someOtherData
    case 2:
      html += "<p>"+ data +"</p>";
      getMoreData(client, onNext);
      break;

    // handle moreData
    case 3:
      html += "<p>"+ data +"</p>";
      res.write(html);
      res.end();
      break;

    // general catch-all error-handler
    default:
      res.statusCode = 500;
      res.end(error.message + '\n' + error.stack);
    }
  };
  onNext();
});
2
répondu kai zhu 2015-12-23 10:44:37

j'ai récemment créé une abstraction plus simple appelée attendez.pour appeler les fonctions asynchrones en mode sync (basé sur les fibres). C'est à un stade précoce, mais fonctionne. Il est à:

https://github.com/luciotato/waitfor

par attendre.pour , vous pouvez appeler n'importe quelle fonction async standard de nodejs, comme si c'était une fonction de synchronisation.

par attendre.pour votre code pourrait être:

var http=require('http');
var wait=require('wait.for');

http.createServer(function(req, res) {
  wait.launchFiber(handleRequest,req, res); //run in a Fiber, keep node spinning
}).listen(8080);


//in a fiber
function handleRequest(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  var someData = wait.for(getSomeDate,client);
  html += "<p>"+ someData +"</p>";
  var someOtherData = wait.for(getSomeOtherDate,client);
  html += "<p>"+ someOtherData +"</p>";
  var moreData = wait.for(getMoreData,client);
  html += "<p>"+ moreData +"</p>";
  res.write(html);
  res.end();
};

...ou si vous voulez être moins verbeux (et aussi ajouter la prise d'erreur)

//in a fiber
function handleRequest(req, res) {
  try {
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write(
    "<h1>Demo page</h1>" 
    + "<p>"+ wait.for(getSomeDate,client) +"</p>"
    + "<p>"+ wait.for(getSomeOtherDate,client) +"</p>"
    + "<p>"+ wait.for(getMoreData,client) +"</p>"
    );
    res.end();
  }
  catch(err) {
   res.end('error '+e.message); 
  }

};

dans tous les cas, getSomeDate , getSomeOtherDate et getMoreData devrait être standard async fonctions avec le dernier paramètre a fonction callback (err,data)

comme dans:

function getMoreData(client, callback){
  db.execute('select moredata from thedata where client_id=?',[client.id],
       ,function(err,data){
          if (err) callback(err);
          callback (null,data);
        });
}
1
répondu Lucio M. Tato 2013-08-23 01:15:08

pour résoudre ce problème j'ai écrit nodent ( https://npmjs.org/package/nodent ) qui pré-traite invisiblement votre JS. Votre exemple de code pourrait devenir (async, vraiment lire les docs).

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  someData <<= getSomeDate(client) ;

  html += "<p>"+ someData +"</p>";
  someOtherData <<= getSomeOtherDate(client) ;

  html += "<p>"+ someOtherData +"</p>";
  moreData <<= getMoreData(client) ;

  html += "<p>"+ moreData +"</p>";
  res.write(html);
  res.end();
});

évidemment, il y a beaucoup d'autres solutions, mais le pré-traitement a l'avantage d'avoir peu ou pas de temps d'exécution et grâce au support source-map, il est facile de déboguer aussi.

1
répondu MatAtBread 2014-01-04 20:25:20

j'ai eu le même problème. J'ai vu les libs principaux pour le noeud exécuter des fonctions asynchrones, et ils présentent donc un chaînage non-naturel (vous devez utiliser trois ou plusieurs méthodes confs etc) pour construire votre code.

j'ai passé quelques semaines à développer une solution simple et facile à lire. S'il vous plaît, essayez de donner une EnqJS . Tous les avis seront appréciées.

au lieu de:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  getSomeDate(client, function(someData) {
    html += "<p>"+ someData +"</p>";
    getSomeOtherDate(client, function(someOtherData) {
      html += "<p>"+ someOtherData +"</p>";
      getMoreData(client, function(moreData) {
        html += "<p>"+ moreData +"</p>";
        res.write(html);
        res.end();
      });
    });
  });

avec EnqJS:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";

  enq(function(){
    var self=this;
    getSomeDate(client, function(someData){
      html += "<p>"+ someData +"</p>";
      self.return();
    })
  })(function(){
    var self=this;
    getSomeOtherDate(client, function(someOtherData){ 
      html += "<p>"+ someOtherData +"</p>";
      self.return();
    })
  })(function(){
    var self=this;
    getMoreData(client, function(moreData) {
      html += "<p>"+ moreData +"</p>";
      self.return();
      res.write(html);
      res.end();
    });
  });
});

Observe que le code semble plus grand qu'avant. Mais il n'est pas imbriquées comme avant. Pour paraître plus naturel, les chaînes sont appelées immédiatement:

enq(fn1)(fn2)(fn3)(fn4)(fn4)(...)

et de dire qu'il est retourné, à l'intérieur de la fonction que nous appelons:

this.return(response)
0
répondu Thadeu de Paula 2011-10-09 15:21:29

je le fais d'une manière assez primitive mais efficace. Par exemple: J'ai besoin d'un modèle avec ses parents et ses enfants et disons que je dois faire des requêtes séparées pour eux:

var getWithParents = function(id, next) {
  var getChildren = function(model, next) {
        /*... code ... */
        return next.pop()(model, next);
      },
      getParents = function(model, next) {
        /*... code ... */
        return next.pop()(model, next);
      }
      getModel = function(id, next) {
        /*... code ... */
        if (model) {
          // return next callbacl
          return next.pop()(model, next);
        } else {
          // return last callback
          return next.shift()(null, next);
        }
      }

  return getModel(id, [getParents, getChildren, next]);
}
0
répondu mvbl fst 2012-07-15 04:51:12

utiliser des fibres https://github.com/laverdet/node-fibers il rend le code asynchrone ressemble à synchrone (sans blocage)

j'utilise personnellement ce petit emballage http://alexeypetrushin.github.com/synchronize Exemple de code de mon projet (chaque méthode est en fait asynchrone, en travaillant avec le fichier async IO) j'ai même peur d'imaginer quel gâchis ce serait avec les bibliothèques de callback ou async-control-flow helper.

_update: (version, changesBasePath, changes, oldSite) ->
  @log 'updating...'
  @_updateIndex version, changes
  @_updateFiles version, changesBasePath, changes
  @_updateFilesIndexes version, changes
  configChanged = @_updateConfig version, changes
  @_updateModules version, changes, oldSite, configChanged
  @_saveIndex version
  @log "updated to #{version} version"
0
répondu Alexey Petrushin 2012-10-26 23:55:06

tâche.js vous offre ceci:

spawn(function*() {
    try {
        var [foo, bar] = yield join(read("foo.json"),
                                    read("bar.json")).timeout(1000);
        render(foo);
        render(bar);
    } catch (e) {
        console.log("read failed: " + e);
    }
});

au lieu de ceci:

var foo, bar;
var tid = setTimeout(function() { failure(new Error("timed out")) }, 1000);

var xhr1 = makeXHR("foo.json",
                   function(txt) { foo = txt; success() },
                   function(err) { failure() });
var xhr2 = makeXHR("bar.json",
                   function(txt) { bar = txt; success() },
                   function(e) { failure(e) });

function success() {
    if (typeof foo === "string" && typeof bar === "string") {
        cancelTimeout(tid);
        xhr1 = xhr2 = null;
        render(foo);
        render(bar);
    }
}

function failure(e) {
    xhr1 && xhr1.abort();
    xhr1 = null;
    xhr2 && xhr2.abort();
    xhr2 = null;
    console.log("read failed: " + e);
}
0
répondu Janus Troelsen 2013-06-11 16:26:41

après que les autres ont répondu, Vous avez déclaré que votre problème étaient des variables locales. Il semble être un moyen facile de le faire est d'écrire une fonction externe pour contenir ces variables locales, puis d'utiliser un tas de nommé les fonctions internes et l'accès par nom. De cette façon, vous ne nicherez jamais que deux profonds, indépendamment du nombre de fonctions que vous devez enchaîner ensemble.

Voici la tentative de mon internaute novice d'utiliser le noeud mysql .module js avec emboîtement:

function with_connection(sql, bindings, cb) {
    pool.getConnection(function(err, conn) {
        if (err) {
            console.log("Error in with_connection (getConnection): " + JSON.stringify(err));
            cb(true);
            return;
        }
        conn.query(sql, bindings, function(err, results) {
            if (err) {
                console.log("Error in with_connection (query): " + JSON.stringify(err));
                cb(true);
                return;
            }
            console.log("with_connection results: " + JSON.stringify(results));
            cb(false, results);
        });
    });
}

ce qui suit est une réécriture utilisant des fonctions internes nommées. La fonction externe with_connection peut également être utilisée comme support pour les variables locales. (Ici, j'ai les paramètres sql , bindings , cb cela agit de la même manière, mais vous pouvez simplement définir quelques variables locales supplémentaires dans with_connection .)

function with_connection(sql, bindings, cb) {

    function getConnectionCb(err, conn) {
        if (err) {
            console.log("Error in with_connection/getConnectionCb: " + JSON.stringify(err));
            cb(true);
            return;
        }
        conn.query(sql, bindings, queryCb);
    }

    function queryCb(err, results) {
        if (err) {
            console.log("Error in with_connection/queryCb: " + JSON.stringify(err));
            cb(true);
            return;
        }
        cb(false, results);
    }

    pool.getConnection(getConnectionCb);
}

j'avais pensé que peut-être il serait possible de faire un objet avec des variables d'instance, et d'utiliser ces variables d'instance en remplacement des variables locales. Mais maintenant je trouve que l'approche ci-dessus en utilisant des fonctions imbriquées et des variables locales est plus simple et plus facile à comprendre. Il faut un certain temps pour désapprendre OO, il me semble :-)

voici Donc ma version précédente avec un objet et des variables d'instance.

function DbConnection(sql, bindings, cb) {
    this.sql = sql;
    this.bindings = bindings;
    this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
    var self = this;
    if (err) {
        console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    conn.query(this.sql, this.bindings, function(err, results) { self.query(err, results); });
}
DbConnection.prototype.query = function(err, results) {
    var self = this;
    if (err) {
        console.log("Error in DbConnection.query: " + JSON.stringify(err));
        self.cb(true);
        return;
    }
    console.log("DbConnection results: " + JSON.stringify(results));
    self.cb(false, results);
}

function with_connection(sql, bindings, cb) {
    var dbc = new DbConnection(sql, bindings, cb);
    pool.getConnection(function (err, conn) { dbc.getConnection(err, conn); });
}

Il s'avère que bind peut être utilisé pour un certain avantage. Il permet de me débarrasser de la un peu laid anonyme des fonctions que j'ai créées et qui n'ont rien fait d'autre que de se transmettre à un appel de méthode. Je ne pouvais pas passer la méthode directement parce qu'il aurait été impliqué avec la mauvaise valeur de this . Mais avec bind , je peux spécifier la valeur de this que je veux.

function DbConnection(sql, bindings, cb) {
    this.sql = sql;
    this.bindings = bindings;
    this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
    var f = this.query.bind(this);
    if (err) {
        console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    conn.query(this.sql, this.bindings, f);
}
DbConnection.prototype.query = function(err, results) {
    if (err) {
        console.log("Error in DbConnection.query: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    console.log("DbConnection results: " + JSON.stringify(results));
    this.cb(false, results);
}

// Get a connection from the pool, execute `sql` in it
// with the given `bindings`.  Invoke `cb(true)` on error,
// invoke `cb(false, results)` on success.  Here,
// `results` is an array of results from the query.
function with_connection(sql, bindings, cb) {
    var dbc = new DbConnection(sql, bindings, cb);
    var f = dbc.getConnection.bind(dbc);
    pool.getConnection(f);
}

bien sûr, rien de tout cela n'est js avec noeud.js codage -- je viens de passer une couple d'heures. Mais peut-être qu'avec un peu de polissage cette technique peut aider?

0
répondu hibbelig 2013-10-22 20:44:07

async.js fonctionne bien pour cela. Je suis tombé sur cet article très utile qui explique la nécessité et l'utilisation d'async.js avec exemples: http://www.sebastianseilund.com/nodejs-async-in-practice

0
répondu learner_19 2014-02-28 17:48:42

si vous ne voulez pas utiliser" step "ou" seq", veuillez essayer" line " qui est une fonction simple pour réduire le rappel asynchrone.

https://github.com/kevin0571/node-line

0
répondu Kevin 2015-01-27 10:13:15

C#-comme asyncawait est une autre manière de faire

https://github.com/yortus/asyncawait

async(function(){

    var foo = await(bar());
    var foo2 = await(bar2());
    var foo3 = await(bar2());

}
0
répondu Artur Stary 2015-03-03 16:07:38

utilisant fil votre code ressemblerait à ceci:

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});

    var l = new Wire();

    getSomeDate(client, l.branch('someData'));
    getSomeOtherDate(client, l.branch('someOtherData'));
    getMoreData(client, l.branch('moreData'));

    l.success(function(r) {
        res.write("<h1>Demo page</h1>"+
            "<p>"+ r['someData'] +"</p>"+
            "<p>"+ r['someOtherData'] +"</p>"+
            "<p>"+ r['moreData'] +"</p>");
        res.end();
    });
});
0
répondu Dan Key 2015-10-19 16:28:32

pour vous, considérez le Jazz.js https://github.com/Javanile/Jazz.js/wiki/Script-showcase


    const jj = require('jazz.js');

    // ultra-compat stack
    jj.script([
        a => ProcessTaskOneCallbackAtEnd(a),
        b => ProcessTaskTwoCallbackAtEnd(b),
        c => ProcessTaskThreeCallbackAtEnd(c),
        d => ProcessTaskFourCallbackAtEnd(d),
        e => ProcessTaskFiveCallbackAtEnd(e),
    ]);

0
répondu cicciodarkast 2016-10-07 14:54:40