【问题标题】:JS Closures, Redis, loop, Async :: empty arrayJS 闭包,Redis,循环,异步 :: 空数组
【发布时间】:2013-06-12 10:43:44
【问题描述】:

我放弃了。请一些聪明的stackoverflow僧侣修复我的错误?

代码是自我解释的。客户端发送房间名称,服务器进行 redis 查找并将有效房间推送到数组。添加所有房间后,列表应发送给客户端。

问题是基于闭包、异步等。我理解这个问题,但无法解决问题,因为数组需要保留在函数内。棘手。

代码:

function roomList(socket){

  var roomlist = [], rooms = getRooms(), p = /pChannel_/;

  redis.select(7, function(err,res){

    for (var k in rooms){

      if(rooms[k] != '' && p.test(rooms[k])){

        var key = 'channel:'+rooms[k];

        redis.hgetall(key, function (err, reply) { 

          if(reply){ 
            var c = io.sockets.manager.rooms[rooms[k]];
            roomlist.push( Array(reply['name'],c.length,reply['icon']) );
          }
          else { console.log('nothing found'); }

        });

      }

    }

    // here be dragons
    console.log(roomlist);
    socket.emit('roomList', roomlist);

  });

}

谢谢。

【问题讨论】:

    标签: node.js loops asynchronous redis closures


    【解决方案1】:

    来吧,伙计们。 OP 明确表示她/他对了解事情应该如何运作感兴趣。而且您不需要 Q 或 async 或任何其他 3rd 方模块来实现这一点。

    在初始代码中,有两个问题:

    • 使用 Javascript,闭包范围是函数级别,而不是块级别。必须引入一个函数来定义一个适当的闭包。这里可以使用一个简单的forEach。

    • 在收到来自 Redis 的回复后,不会运行最后一步(即发出)。它必须在循环本身中调用。为了实现它,需要对item进行计数,以便内部回调可以测试过程是否完成。

    所以这是另一个版本:

    function roomList(socket){
    
      var roomlist = [], rooms = getRooms(), p = /pChannel_/;
    
      redis.select(7, function(err,res){
        var count = rooms.length
        rooms.forEach( function(r) {
          if( r != '' && p.test(r) ) {
            var key = 'channel:'+r
            redis.hgetall(key, function (err, reply) { 
              if(reply) { 
                var c = io.sockets.manager.rooms[r];
                roomlist.push( Array(reply['name'],c.length,reply['icon']) );
              } else {
                console.log('nothing found');
              }
              if ( --count <= 0 ) {
                // here be dragons
                console.log(roomlist);
                socket.emit('roomList', roomlist);
              }
            });
          } else --count;
        });
      });
    }
    

    【讨论】:

    • 你们在开玩笑吗?^^ 数数不是“乱码”吗?当然它解决了问题..
    • 嗯,就个人而言,这就是我会使用的,这是纯粹的延续传递风格,不依赖任何花哨的库。
    • 我可能在 5 或 6 年前使用过 count 方法,但现在不行 - 至少不会,除非我被退回到一个寒冷的黑暗角落。
    • @DidierSpezia 您的解决方案不保留顺序,这可能(也可能不会)是一个问题。就个人而言,我发现跟踪计数会导致代码混乱。此外,OP 表示他理解问题,但正在努力寻找答案。我们提供了一个。
    • 这样的东西行不通? codereply = function() { return redis.hgetall(key, function (err, reply) {return reply}) }code @robertklep 问题是从 redis.get 函数的闭包中获取返回值.所以计数必须被视为一种黑客行为。编辑:好的。我的帖子中没有定义。
    【解决方案2】:

    如果您只是想在发出响应之前等待房间列表完全建立(这似乎非常合理),并假设 Q 可用,那么您应该只需要几行额外的 Q 魔法加上一个闭包- 围绕内部代码形成包装器,以维护对在 for 循环的每次传递中延迟的 Q 的可靠引用。

    function roomList(socket) {
        redis.select(7, function(err, res) {
            var list = [],
                rooms = getRooms(),
                p = /pChannel_/,
                promises = [];
            for(var k in rooms) {
                if(rooms[k] != '' && p.test(rooms[k])) {
                    (function(dfrd) {
                        promises.push(dfrd.promise);
                        var key = 'channel:' + rooms[k];
                        redis.hgetall(key, function(err, reply) {
                            if(reply) {
                                var c = io.sockets.manager.rooms[rooms[k]];
                                list.push( [reply['name'], c.length, reply['icon']] );
                            }
                            else {
                                console.log('nothing found');
                            }
                            dfrd.resolve();
                        });
                    })(Q.defer());
                }
            }
            Q.all(promises).then(function() {
                console.log(list);
                socket.emit('roomList', list);
            });
        });
    }
    

    【讨论】:

    • 没有可用的 Q。但我发现这种情况比我想象的要复杂一些。
    • 没有 Q 或类似的东西你会很挣扎。
    【解决方案3】:

    看起来像是async.map 的工作:

    function roomList(socket){
      var rooms = getRooms(), p = /pChannel_/;
    
      redis.select(7, function(err, res) {
        async.map(rooms, function(room, callback) {
          if (room === '' || ! p.test(room))
            return callback(null, null);
    
          var key = 'channel:' + room;
          var c   = io.sockets.manager.rooms[room];
          redis.hgetall(key, function (err, reply) {
            if (err)
              callback(err); // propagate Redis errors to final callback, don't know
                             // if you want that or not; use 'callback(null)' if not.
            else
            if (reply)
              callback(err, Array(reply.name, c.length, reply.icon) );
            else
              callback(err, null);
          });
        }, function(err, roomlist) {
          if (err)
             // handle Redis errors...
    
          // filter 'null' entries from roomlist
          roomlist = roomlist.filter(function(room) { return room !== null });
          console.log(roomlist);
          socket.emit('roomList', roomlist);
        });
      });
    }
    

    (未经测试)

    【讨论】:

    • 看起来是一个很好的解决方案,但这只会让我了解理解的过程。我更喜欢没有外部方法的方法,如地图或瀑布。一些伪代码/提示很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-16
    • 2017-12-12
    • 2019-10-08
    • 1970-01-01
    • 2018-10-17
    • 2021-05-24
    相关资源
    最近更新 更多