Javascript Node.js中处理异步循环的最佳模式

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

我是Node的新手,并试图确保我对JSON驱动的web应用程序使用了合理的设计

我在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获取一组键,然后请求每一个键,将结果流式输出到半手动创建的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(',') + ']');
            }
        });
    });
});