请问有关node和redis的控制流问题的一些建议吗? (也就是Python编码器试图习惯JavaScript)
我不明白为什么client.smembers
和client.get
(Redis查找)需要回调而不仅仅是语句 - 这会让生活变得非常复杂。
基本上我想查询一个集合,然后当我得到集合的结果时,我需要为每个结果执行get。 当我获得所有数据时,我需要将其广播回客户端。
目前我在两个回调中使用全局对象执行此操作,这看起来很混乱。我甚至不确定它是否安全(代码是否会在启动另一个之前等待一个client.get
完成?)。
目前的代码如下:
var all_users = [];
// Get all the users for this page.
client.smembers("page:" + current_page_id, function (err, user_ids ) {
// Now get the name of each of those users.
for (var i = 0; i < user_ids.length; i++) {
client.get('user:' + user_ids[i] + ':name', function(err, name) {
var myobj = {};
myobj[user_ids[i]] = name;
all_users.push(myobj);
// Broadcast when we have got to the end of the loop,
// so all users have been added to the list -
// is this the best way? It seems messy.
if (i === (user_ids.length - 1)) {
socket.broadcast('all_users', all_users);
}
});
}
});
但这看起来非常混乱。这真的是最好的方法吗?在调用socket.broadcast
之前,如何确保所有查找都已执行?
刮擦头提前感谢任何建议。
答案 0 :(得分:1)
我不明白为什么
client.smembers
和client.get
(Redis查找)需要回调而不仅仅是语句 - 这会让生活变得非常复杂。
这就是Node。 (我很确定这个话题已在这里讨论了很多次,看看其他问题,它肯定在那里)
如何在调用
socket.broadcast
之前确定所有查找都已执行?
这就是回调函数中的err
。这是Node的标准 - 回调中的第一个参数是错误对象(null
如果一切正常)。所以只需使用这样的东西,确保没有发生错误:
if (err) {
... // handle errors.
return // or not, it depends.
}
... // process results
但这似乎非常混乱。
你会习惯的。我实际上发现它很好,代码格式良好,项目结构巧妙。
其他方式是:
答案 1 :(得分:0)
如果你完全不喜欢写回调式的东西,你可能想尝试streamlinejs:
var all_users = [];
// Get all the users for this page.
var user_ids = client.smembers("page:" + current_page_id, _);
// Now get the name of each of those users.
for (var i = 0; i < user_ids.length; i++) {
var name = client.get('user:' + user_ids[i] + ':name', _);
var myobj = {};
myobj[user_ids[i]] = name;
all_users.push(myobj);
}
socket.broadcast('all_users', all_users);
请注意,此变体的缺点是一次只能获取一个用户名。此外,您仍应了解此代码的真正功能。
答案 2 :(得分:0)
Async是一个很棒的图书馆,你应该看看。为什么?清洁代码/流程/易于跟踪..等等
另外,请记住,在for循环之后将处理所有异步函数。在你的例子中,它可能导致错误的“i”值。使用闭包:
for (var i = 0; i < user_ids.length; i++) { (function(i) {
client.get('user:' + user_ids[i] + ':name', function(err, name) {
var myobj = {};
myobj[user_ids[i]] = name;
all_users.push(myobj);
// Broadcast when we have got to the end of the loop,
// so all users have been added to the list -
// is this the best way? It seems messy.
if (i === (user_ids.length - 1)) {
socket.broadcast('all_users', all_users);
}
});
})(i)}
你应该知道什么时候完成它是使用像async(我认为)那样的递归模式。它比你自己做的简单得多。
async.series({
getMembers: function(callback) {
client.smembers("page:" + current_page_id, callback);
}
}, function(err, results) {
var all_users = [];
async.forEachSeries(results.getMembers, function(item, cb) {
all_users.push(item);
cb();
}, function(err) {
socket.broadcast('all_users', all_users);
});
});
此代码可能无效,但您应该能够弄清楚如何执行此操作。
步骤库也很好(我认为只有30行代码)
答案 3 :(得分:0)
我不明白为什么client.smembers和client.get(Redis查找) 需要成为回调,而不是简单地成为语句-它使得 生活非常复杂。
是的,所以每个人都同意回调地狱不是布宜诺斯艾利斯。在撰写本文时,回调是Node的垂死功能。不幸的是,Redis库不支持返回Promises。
但是您可能需要一个模块,如下所示:
const util = require("util");
这是一个标准库,包含在Node运行时中,并具有许多可以使用的实用程序功能,其中一个是“ promisify”: https://nodejs.org/api/util.html#util_util_promisify_original
现在,当您七年前问这个问题时, util.promisify(原始)不存在,因为它是在- v 8.0.0版本中添加的。 strong>,因此我们现在可以使用更新的答案来更新此问题。
因此 promisify 是一个函数,我们可以将其传递给client.get()
之类的函数,它将返回一个具有讨厌的回调行为的新函数,而是将其包装得整洁美观。让它返回一个承诺。
因此 promisify 接受接受回调作为最后一个参数的任何函数,并使其返回一个Promise,这听起来像是您七年前想要的确切行为,而今天我们已经提供了。
const util = require("util");
client.get = util.promisify(client.get);
因此,我们将对.get()
函数的引用传递给util.promisify()
。
这将接受您的函数,将其包装起来,因此它不执行回调,而是返回Promise。因此util.promisify()
返回一个已经已承诺的新函数。
因此,您可以采用该新功能并覆盖client.get()
上的现有功能。
如今,您不必为Redis查找使用回调。因此,现在您可以像这样使用async/await
语法:
const cachedMembers = await client.get('user:' + user_ids[i]);
因此,我们等待解决该问题,并将其解决的所有问题分配给cachedMembers
。
使用ES6数组辅助方法而不是for
循环,甚至可以进一步清理代码以进行更多更新。我希望这个答案对当前用户有用,否则OP已过时。