我如何使用arr.forEach来调用asynchronousJavaScript的redis调用?
我正在使用node.js和redis。 我有一堆key的redis数据库。 像这样的东西:
用户/克里斯/药水用户/皮特/药水用户/克里斯/种族用户/皮特/种族用户/克里斯/武器用户/皮特/武器
我想做一个redis调用,它检索所有的用户状态,把统计信息放到一个JS对象中,然后传递给客户端在浏览器中显示字符统计信息。 使用javascript我把用户名chris注入到redis调用中,像这样:
KEYS user/u/*
它返回:
1) "user/chris/weapon" 2) "user/chris/race" 3) "user/chris/potion"
现在我可以迭代这些结果,用GET获取每个键的值,并创build一个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)
被调用三次。 不止一次调用callback将会产生问题,因为最终callback会触发将数据发送到客户端的数据。
我想我知道发生了什么。 client.GET
运行三次,因为我问它。 replies
数组有三个元素,所以每次client.GET
的结果都会被调用。
我需要做的是让forEach循环遍历所有的redis键,一旦完成,就调用一次callback函数。 我试着用promise来解决这个问题,然后用async来解决这个问题,因为async有async.each。 我被困在解决这两个问题。 我现在回到原点,我相信我必须做一些与forEach不同的事情来取得进展。
我怎样才能做到这一点?
由于您正在迭代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) }); }); });
我知道帮助@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)解决了asynchronous问题。
返回的json将拥有所有的“user:chris”属性,所以你可以在客户端浏览器中做任何你想做的事情。