我最近一直在更深入地探索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基本上是我的,除了我想弄清楚如何设计它而不依赖于同步库。
答案 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 答案 1 :(得分:2)
在不使用其他库的情况下实现此目的的一种方法是使用“递归模式”。
你不能在for循环中做一些异步调用。
card_keys 需要是一个数组。
关于如何编写for循环的一些有用的链接:
编辑:像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查询返回结果之前响应,因此您没有正确执行此操作。
您应该使用Step或Async等控制流库。在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);
}
);
}
);
});