在Node.js中处理asynchronous循环的最佳模式

我是新来的Node,并试图确保我正在使用JSON驱动的Web应用程序的理智的devise。

我有一堆存储在Redis中的数据,并且通过节点检索它,并从Redis传出结果。 这是我正在做的一个很好的例子:

app.get("/facility", function(req, res) { rc.keys("FACILITY*", function(err, replies) { res.write("["); replies.forEach(function (reply, i) { rc.get(reply, function(err, reply) { res.write(reply); if (i == replies.length-1) { res.write("]"); res.end(); } else res.write(","); }); }); }); }); 

本质上,我从Redis获取一组密钥,然后请求每个密钥,将结果stream出到半手动创build的JSON(从Redis出来的string已经在JSON中)。 现在这个效果很好,但是我不禁想到i == respond.length-1有点不整齐?

我可以使用Redis中的mget来完成所有这些,但这并不是我想要得到的。 这是如何最好地处理与forEach的asynchronous循环,stream输出和正常closures与res.end循环连接完成。

这是最好的方式,还是有一个更优雅的模式,我可以遵循?

上面的代码可能不会做你期望的。 您按顺序开始每个.get() ,但是它们可能不会按顺序callback – 所以结果可能以任何顺序stream出。 如果您想要将结果stream式传输,而不是将其收集到内存中,则需要依次使用.get()

我认为, caolan的asynchronous库使得很多这个更容易。 以下是您可以使用它按顺序获取每个项目的一种方法(警告,未经testing):

 app.get("/facility", function(req, res) { rc.keys("FACILITY*", function(err, replies) { var i = 0; res.write("["); async.forEachSeries(replies, function(reply, callback){ rc.get(reply, function(err, reply) { if (err){ callback(err); return; } res.write(reply); if (i < replies.length) { res.write(","); } i++; callback(); }); }, function(err){ if (err) { // Handle an error } else { res.end(']'); } }); }); }); 

如果你不关心订单,只需使用async.forEach()

如果你不介意收集结果,并希望他们顺序返回,你可以像这样使用async.map() (警告,也未经testing):

 app.get("/facility", function(req, res) { rc.keys("FACILITY*", function(err, replies) { async.map(replies, rc.get.bind(rc), function(err, replies){ if (err) { // Handle an error } else { res.end('[' + replies.join(',') + ']'); } }); }); }); 

您可以使用asynchronous库,它提供了一些方便的循环方法,如forEach:

forEach(arr,iterator,callback)

并行地将迭代器函数应用于数组中的每个项目。 迭代器用列表中的项目和callback完成时调用。 如果迭代器将错误传递给此callback函数,forEach函数的主callback函数将立即被调用并返回错误。

请注意,由于此函数并行地将迭代器应用于每个项目,因此不能保证迭代器函数将按顺序完成。

 // assuming openFiles is an array of file names and saveFile is a function // to save the modified contents of that file: async.forEach(openFiles, saveFile, function(err){ // if any of the saves produced an error, err would equal that error }); 

但我不禁认为,我==回复。长度-1是有点不整洁?

我听到很多人这样说。 这是我将如何手工完成的:

 app.get("/facility", function(req, res, next) { rc.keys("FACILITY*", function(err, replies) { if (err) return next(err); var pending = replies.length; res.write("["); replies.forEach(function (reply) { rc.get(reply, function(err, reply) { res.write(reply); if (!--pending) { res.write("]"); return res.end(); } res.write(","); }); }); }); }); 

显然手工做得不太好,这就是为什么人们把它抽象成图书馆或者其他function。 但喜欢与否,这就是你如何做一个asynchronous并行循环。 🙂

您可以使用前面提到的asynchronous库来隐藏讨厌的内部。