在我的应用中,我有以下内容:
client.on('test', function(req, fn) {
var returnArr = [];
redis.hkeys(req, function (err, replies) {
replies.forEach(function(reply, i) {
if (reply.indexOf('list.') > -1) {
redis.hgetall(reply.substring(5), function(err, r) {
returnArr.push({name:r['name'],index:i});
console.log(returnArr);
});
}
});
console.log(returnArr);
});
console.log(returnArr);
});
由于某种原因,第二个和第三个日志包含一个空白数组,即使该数组在事件开始时被声明一次。有什么想法吗?
编辑:对不起,我在没有想到的时候在这里发布了变量名称。当它被命名为任何东西时会发生这种情况。答案 0 :(得分:3)
那些redis调用是异步的。这就是你为他们提供回调的原因。即使您因此修复了变量名,代码也无法正常工作。
详细说明:当数据可用时,将调用“hkeys”回调中的代码。但是,调用将立即返回,因此您的数组在此时将没有任何内容。
您无法在函数中包装异步调用并期望返回值。它根本行不通。
相反,一般模式是完全执行redis API(以及node.js世界中的其他所有内容;实际上就是整点):为自己的函数提供一个回调参数,以便在调用时调用适当。在你的情况下,它将在“hgetall”回调中,这是最后一个被调用的回调。它应该弄清楚你的结果数组中的值和键一样多,所以是时候调用传入函数的回调了。
(我应该注意到,目前还不清楚你要做什么,因为整体功能似乎是对某事的回调。)
另一种方法是使用某种“承诺”模式,尽管这只是对同一想法的重组。
编辑 - 带回调的API的一般模式将是这样的:
function yourAPI( param1, param2, callback ) {
// ...
some.asynchronousFunction( whatever, function( result ) {
callback( result );
}
}
现在,在您的情况下,您正在进行多个异步服务请求,并且您需要确定何时调用回调。我想你可能想要遍历调用中的“回复”来获取密钥并提取你想要获取的列表:
redis.hkeys(req, function (err, replies) {
var keys = [];
replies.forEach(function(reply, i) {
if (reply.indexOf('list.') > -1) {
keys.push( reply.substring(5) );
}
});
keys.forEach( function( key ) {
redis.hgetall(key, function(err, r) {
returnArr.push({name:r['name'],index:i});
if (returnArr.length === keys.length) {
// all values ready
callback( returnArr );
}
});
});
答案 1 :(得分:1)
您无法调用变量return
这是您在代码中无法用作变量的少数保留字之一。
答案 2 :(得分:1)
正如Neal所建议的那样,不要对你的变量使用javascript保留字,这里是列表:
https://developer.mozilla.org/en/JavaScript/Reference/Reserved_Words
答案 3 :(得分:1)
@Pointy已经简洁地回答了这个问题,但让我更清楚地解释一下:那些嵌套函数没有按照你认为的顺序运行。
Node.js是非阻塞,并在准备好时使用Javascript的隐式事件循环来执行它们。这是您的代码,包含行号:
/*01*/ client.on('test', function(req, fn) {
/*02*/ var returnArr = [];
/*03*/ redis.hkeys(req, function (err, replies) {
/*04*/ replies.forEach(function(reply, i) {
/*05*/ if (reply.indexOf('list.') > -1) {
/*06*/ redis.hgetall(reply.substring(5), function(err, r) {
/*07*/ returnArr.push({name:r['name'],index:i});
/*08*/ console.log(returnArr);
/*09*/ });
/*10*/ }
/*11*/ });
/*12*/ console.log(returnArr);
/*13*/ });
/*14*/ console.log(returnArr);
/*15*/ });
/*16*/ //Any other code you have after this.
那么,这件事的执行顺序是什么?
第1行:注册'test'事件的事件处理程序。
第16行:开始运行在此过程中通过事件循环运行的任何其他代码
第2行:事件循环在某个时刻收到了'test'事件,现在正在处理,因此初始化returnArr
第3行:执行非阻塞IO请求,并在正确事件排队到事件循环中时注册回调函数。
第14-15行: last console.log
已执行,此函数已完成运行,应结束当前正在处理的事件。
第4行:请求事件返回并执行回调。 forEach
方法是少数带有回调的阻塞 Node.js方法之一,因此每次回复都会执行每个回调。
第5行:执行if
语句并结束(转到第10行)或进入块(转到第6行)
第6行:执行非阻塞IO请求,向事件循环添加新事件,并在事件返回时运行新回调。
第9行:完成回调注册。
第10行:完成if
声明
第11行:完成`forEach
回调。
第12行:执行第二个 console.log
请求,returnArr
第7行:其中一个事件返回并触发事件处理程序。 returnArr
会获得新数据。
第8行:第一个 console.log
被执行。根据这是什么事件,数组的长度将不同。此外,数组元素 DOES NOT 的顺序必须与replies
数组中列出的回复的顺序相匹配。
基本上,你可以将更深层次嵌套的函数看作之后执行整个不太深层嵌套的函数(因为这就是发生的事情,本质上),无论方法是否包含语句之后是否嵌套非阻塞回调。
如果这让您感到困惑,可以在Continuation Passing Style中编写回调代码,这样很明显外部函数中的所有内容都在内部函数之前执行,或者您可以使用this nice async library来编写你的代码看起来更加迫切。
我认为,这回答了你真实的问题,而不是你输入的问题。