Redis,node.js和Javascript回调范围/流程

时间:2011-11-23 23:47:56

标签: design-patterns node.js socket.io

我最近一直在更深入地探索javascript,使用节点,redis,socket.io,表达简单的webapp。

我认为我遇到的问题是JS和PHP之间的根本区别的一个例子,这是我过去几年一直在使用的。

我已经编写了这个回调函数,需要从redis

返回一堆数据
app.get('/deck', function(request, response) { 
  dealer.sort('cards', 'BY', 'deal:*->timestamp', 'GET', 'card:*->card_key',
    function(err, card_keys) {
      var deck = []; 
      for (k in card_keys) { 
        dealer.hget(card_keys[k], 'data', function(err, card_data) { 
          var deal = eval('(' + card_data +')');
          deals.push(cards);
        }); 
      }   
      response.json(deals);
    }   
  );  
});

现在我首先认为这是一个变量范围问题,所以我用一个闭包重写了(我是否正确地使用了这个术语?)这是不行的,因为我在同步思维集中写这个。 我意识到这基本上是同步的,并在收集数据之前将数据发送出去。设计错了。

我可以重写函数,使用socket.io的emit函数在收集数据时发送数据。

但是有处理同步数据的设计模式吗?如果我想按顺序显示最后10张卡怎么办?我是否总是发送数据并将客户端代码排队10,并触发排序然后控制显示的事件?

编辑:我发现this question基本上是我的,除了我想弄清楚如何设计它而不依赖于同步库。

3 个答案:

答案 0 :(得分:4)

function(err, card_keys) {
    var deck = [];
    for (k in card_keys) {
        // whole bunch of async actions
        dealer.hget(card_keys[k], 'data', function(err, card_data) {
            var deal = eval('(' + card_data + ')');
            deals.push(cards);
        });
    }
    // sync action
    response.json(deals);
}

response.json完成之前,您已经知道hget次了。

答案是引用计数

function(err, card_keys) {
    var deck = [];
    var count = Object.keys(card_keys).length;

    function next() {
        if (--count === 0) {
            response.json(deals.sort(sortThem)); 
        }  
    }

    for (k in card_keys) {
        dealer.hget(card_keys[k], 'data', function(err, card_data) {
            var deal = JSON.parse(card_data);
            deals.push(cards);
            next();
        });
    }
}

你还有两个小问题

  • 使用eval代替JSON.parse
  • 你的交易数组是无序的,因为hget是异步的,所以你需要一个排序函数。

答案 1 :(得分:2)

在不使用其他库的情况下实现此目的的一种方法是使用“递归模式”。

你不能在for循环中做一些异步调用。

card_keys 需要是一个数组。

关于如何编写for循环的一些有用的链接:

  1. http://tech.richardrodger.com/2011/04/21/node-js-%E2%80%93-how-to-write-a-for-loop-with-callbacks/
  2. http://metaduck.com/post/2675027550/asynchronous-iteration-patterns-in-node-js
  3. 编辑:像Raynos所说,你应该使用引用计数。

    我建议您使用lib并使用async执行类似的操作:

    ...
    var deals = []
    async.forEach(card_keys, function(err, results) {
        if (err) {
            callback(err);
        } else {
            deals.push(results); // For each card
        }
    }, function(err) {
        callback(deals); // When it's completed
    });
    ...
    

    这就是async执行forEach的方式

    async.forEach = function (arr, iterator, callback) {
        if (!arr.length) {
            return callback();
        }
        var completed = 0;
        _forEach(arr, function (x) {
            iterator(x, function (err) {
                if (err) {
                    callback(err);
                    callback = function () {};
                }
                else {
                    completed += 1;
                    if (completed === arr.length) {
                        callback();
                    }
                }
            });
        });
    };
    

    编辑:哪一个更快:引用计数或递归模式

    我做了100个基本查询:

    Resurcive pattern:7161,7528,7226 MS

    参考计数:7515,7256,7364 MS

    递归模式代码:

    var req = "select id from membre";
    
    var time = new Date().getTime();
    
    var test = function(value,callback) {
       if(value < 100) {
          client.query(req, function(err, results) {
              test(++value,callback);
          });
       } else {
           callback();
       }
    }
    
    test(0, function() {
       var time2 = new Date().getTime();
       console.log(time2-time);
       client.end();
    });
    

    引用计数代码:

    var req = "select id from membre";
    
    var time = new Date().getTime();
    
    var test = function(callback) {
    
       var c = 100;
       function next() {
          if(--c === 0) callback();
       }
    
      for(var i =0; i < c; i++) {
           client.query(req, function(err) {
              next();
           });
       }
    };
    
    
    test(function() {
       var time2 = new Date().getTime();
        console.log(t2-t);
       client.end();
    });
    

    为什么几乎是同一时间?因为它是 mysql ,我们从这个中的node-js的“async”中获得的收益不多强>情况。见评论。

答案 2 :(得分:1)

由于服务器可以在db查询返回结果之前响应,因此您没有正确执行此操作。

您应该使用StepAsync等控制流库。在dailyjs上了解有关他们的更多信息。

我个人使用Step,所以这里显示我会这样做:

app.get('/deck', function(request, response) { 
  dealer.sort('cards', 'BY', 'deal:*->timestamp', 'GET', 'card:*->card_key',
    function(err, card_keys) {
      var deals = [], iterations = Object.keys(card_keys).length;
      // iterations contains the length of 'card_keys'
      Step(
        function queries() {
          var deck = [];
          if (!iterations) { this(null); return; } // if no cards
          for (k in card_keys) {
            dealer.hget(card_keys[k], 'data', function(err, card_data) {
              var deal;
              if (err) {
                throw new Error('Database error');
              } else {
                // we decrease the number of iterations until it gets to 0
                // and then we trigger the callback, since every query has
                // returned its result
                iterations--;
                deal = eval('(' + card_data +')');
                deals.push(cards);
                if (!iterations) { this(deals); } // callback function called
              }
            });
          }
        },
        function render(deals) {
          response.json(deals);
        }
      );
    }
  );
});