【问题标题】:How can I use arr.forEach to call async JavaScript redis calls?如何使用 arr.forEach 调用异步 JavaScript redis 调用?
【发布时间】:2014-04-29 15:37:57
【问题描述】:

我正在使用 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) 被调用了 3 次。多次调用回调会有问题,因为最终回调会触发数据被发送到客户端。

我想我知道发生了什么。 client.GET 运行了三次,因为我要求它这样做。 replies数组有3个元素,所以每次client.GET的结果进来,都会调用回调。

我需要做的是让 forEach 循环完成对所有 redis 键的迭代,一旦完成,就调用一次回调。我尝试先用 Promise 解决这个问题,然后用 async,因为 async 有 async.each。我被困在解决这两个问题上。我现在又回到了原点,我确信我必须对 forEach 做一些不同的事情才能取得进展。

我怎样才能做到这一点?

【问题讨论】:

  • 将callback(null, userstats); 移到forEach 之外不能解决问题吗?
  • @Greg,这是我尝试的第一件事,但是因为 client.GET(?) 是一个异步函数,所以在 client.GET 返回第一个值之前调用回调。我最终得到一个空的 userstats 对象。
  • 当然!忘记了异步;)

标签: javascript node.js asynchronous redis


【解决方案1】:

由于您正在迭代 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)
             });

         });
    });

【讨论】:

    【解决方案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”属性,因此您可以在客户端浏览器中做任何您想做的事情。

    【讨论】:

      猜你喜欢
      • 2015-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-04
      • 2012-01-20
      • 1970-01-01
      • 2010-10-25
      • 1970-01-01
      相关资源
      最近更新 更多