如何使用arr.forEach调用异步JavaScript redis调用?

时间:2014-03-22 02:51:19

标签: javascript node.js asynchronous redis

我正在使用node.js和redis。我有一堆带有密钥的redis数据库。像这样:

用户/克里斯/药水 用户/皮特/药水 用户/克里斯/种族 用户/皮特/种族 用户/克里斯/武器 用户/皮特/武器

我想做一个redis调用,它检索所有用户统计信息,将统计信息放入JS对象,然后将其传递给客户端,以便在浏览器中显示字符统计信息。使用javascript我将 u 中的用户名chris注入redis调用,如下所示:

KEYS user/u/*

返回:

1) "user/chris/weapon"
2) "user/chris/race"
3) "user/chris/potion"

现在我可以遍历这些结果,使用GET获取每个键的值,并创建一个javascript对象。看起来超级简单,所以我编写了代码。我很快就遇到了使用forEach的问题:

var redis = require('redis');
var client = redis.createClient();

    exports.getUserObject = function(requesteduser, callback) {

        var userstats = {}; // the object to hold the user stats once retrieved from the db
        client.KEYS('user/' + requesteduser + '/*', function(err, replies) {

             replies.forEach(function (reply, i) {
                 client.GET(reply, function(err, value) {
                      // get the key name so we can populate a js object
                      var n = reply.lastIndexOf('/');
                      var key = reply.substring(n + 1, reply.length);
                      userstats[key] = value;

                      console.dir(userstats);

                      callback(null, userstats); // parent expects (err, userstats)
                 });

             });
        });
}

运行时,输出如下:

{ weapon: 'twosword' }
{ weapon: 'twosword', race: 'elf' }
{ weapon: 'twosword', race: 'elf', potion: 'mana' }

callback(null, userstats)被召唤三次。多次调用回调将会有问题,因为最终回调将触发数据被发送到客户端。

我想我知道发生了什么。 client.GET被运行了三次因为我问过它。 replies数组有三个元素,因此每次结果进入client.GET时,都会调用回调。

我需要做的是让forEach循环完成所有redis键的迭代,一旦完成,就调用一次回调。我尝试首先使用promises解决此问题,然后使用async解决此问题,因为async具有async.each。我两次都遇到了解决问题的困难。我现在回到原点,我确信我必须做一些与forEach不同的事情来取得进展。

我该如何做到这一点?

2 个答案:

答案 0 :(得分:2)

由于您正在迭代replies,因此您可以检查何时到达最后一个元素,并且仅在该实例中调用callback

client.KEYS('user/' + requesteduser + '/*', function(err, replies) {

         replies.forEach(function (reply, i) {
             client.GET(reply, function(err, value) {
                  // get the key name so we can populate a js object
                  var n = reply.lastIndexOf('/');
                  var key = reply.substring(n + 1, reply.length);
                  userstats[key] = value;

                  console.dir(userstats);

                  if (i == replies.length-1) callback(null, userstats); // parent expects (err, userstats)
             });

         });
    });

答案 1 :(得分:2)

我知道帮助@Grimtech有点晚了,但我会留下我最终到达这里的其他人的意见:

首先,我宁愿以不同的方式模仿@ Grimtech的问题。像这样:

client.hmset("user:chris", "weapon", "some weapon", "race", "some race", "potion", "some potion");
client.hmset("user:pete", "weapon", "same weapon", "race", "other race", "potion", "other potion");

然后我会在Node中返回一个 json 的方法,假设我可以有一个以“user:chris”开头的键:

router.get('/users/:user_id', function(req, res, next) {
    var response = [];
    var client = redis.createClient();
    client.keys("user:" + req.params.user_id + "*", function (err, users) {
        users.forEach(function (key, pos) {
            client.hgetall(key, function (err, user) {
                response.push(user);
                if (pos === users.length - 1) {
                    res.json(response);
                    client.quit();
                }
            });
        });
    });
});

if(pos === users.length - 1)解决了异步问题。

返回的json将具有所有“user:chris”属性,因此您可以在客户端浏览器中执行任何操作。