【问题标题】:Redis, node.js and Javascript Callback Scoping / FlowRedis、node.js 和 Javascript 回调范围/流
【发布时间】:2011-11-24 17:05:46
【问题描述】:

我最近一直在更深入地探索javascript,玩弄node、redis、socket.io、express simple 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 基本上是我的,除了我想弄清楚如何在不依赖同步库的情况下设计它。

【问题讨论】:

  • 你不能在你的 for 循环中使用异步函数。您应该使用 async 或 streamlinejs 之类的东西。
  • 谢谢,但为了我自己的利益,我想了解如何在不依赖这些库的情况下以回调方式构造它。
  • 检查我发布的两个链接,它应该回答你的问题。

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


【解决方案1】:
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.jsonhget 完成之前触发。

答案是引用计数

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 是异步的,因此您需要一个排序函数。

【讨论】:

  • 感谢您的回答,试图了解这一切。一个简单的问题,为什么 Object.keys(card_keys).length 而不是 card_keys.length?
  • @jskulski 不确定card_keys 是否是一个数组。我的意思是你确实在它上面使用了for..in 循环,而在数组上使用for..in 是一种不好的做法
  • @Raynos 好吧,我对此很陌生 :) 感谢所有提示,不胜感激。
【解决方案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

编辑:就像 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
});
...

这就是异步执行 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 个基本查询:

复活模式: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 我们并没有从 this 中 node-js 的“异步”中获得太多收益强>情况。见 cmets。

【讨论】:

  • “递归模式”是错误的,这不是异步的,那是瀑布式的,而且慢得要命。
  • 好点。我不会说慢得要命......但引用计数更好,很多
  • 递归意味着一次只运行一个任务。这意味着零并行性,这意味着慢得要命
  • 我明白为什么你的代码没有跑得更快。这是因为您使用 mysql 并且它正在使用事务。因此,即使引用计数代码并行发送 100 个 mysql 请求,mysql 一次也只处理一个。这就是我们使用真实数据库的原因,它是一段阻塞代码 (mysql) 如何成为整个应用程序瓶颈的一个很好的例子
【解决方案3】:

您没有正确执行此操作,因为服务器可以在 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);
        }
      );
    }
  );
});

【讨论】:

    猜你喜欢
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多