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.
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.
Kay, il suffit d'utiliser l'un de ces modules.
- "151960920-débit" js
- funk
- futures
- groupie
- nœud-continuables
- Slide
- Étape
- nœud-encaissement
- async.js
- async
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()
}
);
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!
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()();
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.
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);
}
};
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();
});
});
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.
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.
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();
}
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();
});
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);
});
}
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.
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)
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]);
}
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"
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);
}
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?
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
si vous ne voulez pas utiliser" step "ou" seq", veuillez essayer" line " qui est une fonction simple pour réduire le rappel asynchrone.
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());
}
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();
});
});
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), ]);