Javascript Node.js中处理异步循环的最佳模式
我是Node的新手,并试图确保我对JSON驱动的web应用程序使用了合理的设计 我在Redis中存储了大量数据,并通过节点检索这些数据,当结果来自Redis时,流式输出结果。下面是我正在做的一个很好的例子:Javascript Node.js中处理异步循环的最佳模式,javascript,node.js,redis,Javascript,Node.js,Redis,我是Node的新手,并试图确保我对JSON驱动的web应用程序使用了合理的设计 我在Redis中存储了大量数据,并通过节点检索这些数据,当结果来自Redis时,流式输出结果。下面是我正在做的一个很好的例子: app.get("/facility", function(req, res) { rc.keys("FACILITY*", function(err, replies) { res.write("["); replies.forEach(functi
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获取一组键,然后请求每一个键,将结果流式输出到半手动创建的JSON中(从Redis生成的字符串已经是JSON)。现在,这很好地工作了,但是我忍不住认为I==repress.length-1有点不整洁
我可以用Redis中的mget来完成所有这些,但这并不是我想要达到的目的;这是使用forEach处理异步循环的最佳方式,流化输出并优雅地关闭与res的连接。循环结束
这是最好的方法,还是我可以遵循更优雅的模式?您可以使用该库,它提供了一些方便的循环方法,例如forEach:
forEach(arr、迭代器、回调)
将迭代器函数并行应用于数组中的每个项。
使用列表中的一个项和
当它完成时。如果迭代器将错误传递给
回调时,forEach函数的主回调立即
调用时出错
请注意,由于此函数将迭代器应用于
并行迭代器函数不能保证
按顺序完成
示例
// 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
});
但是我忍不住想I==repress.length-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(",");
});
});
});
});
显然,手工操作并不太漂亮,这就是为什么人们将其抽象为库或其他功能。但不管你喜欢与否,这就是异步并行循环的实现方式。:)
您可以使用前面提到的库来隐藏令人讨厌的内部信息。上述代码可能无法实现您的预期。您正在按顺序启动每个
.get()
,但它们可能不会按顺序回调-因此结果可以按任何顺序流出来。如果要流式处理结果而不是在内存中收集结果,则需要按顺序.get()
我想这会让很多事情变得容易。这里有一种方法可用于按顺序获取每个项目(警告,未测试):
对于深层嵌套函数回调,我将使用async.js库;谢谢你的代码;我试用了异步库,它工作得非常好。顺序并不重要,但是map解决方案看起来更优雅,所以我可能会坚持下去;这是一种巧妙的方法。你能解释一下它是如何工作的吗?@mjs是ECMAScript 5的一部分,它返回一个函数的副本,该函数“绑定”到一个特定的
值。在这种情况下,这意味着当async.map调用get()
时,它将有rc
作为它的this
值。@mjs通常,函数中this
的值取决于调用它的方式-调用对象上的函数(如rc.get()
)将函数内的此
值设置为rc
。这是在通话时间确定的。如果您要执行类似于var cb=rc.get;cb()。同样,如果您要执行var foo={};foo.get=rc.get;foo.get()
,get
将使用foo
作为上下文,而不是rc
,并且可能会表现得非常非常糟糕。感谢您的详细解释;谢谢。
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(']');
}
});
});
});
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(',') + ']');
}
});
});
});