【问题标题】:node.js/socket.io disappearing variablenode.js/socket.io 消失变量
【发布时间】:2012-03-20 16:10:30
【问题描述】:

在我的应用中,我有以下内容:

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);
    });

由于某种原因,第二个和第三个日志包含一个空白数组,即使该数组在事件开始时声明了一次。有什么想法吗?

编辑:对不起,我在此处发布时更改了变量名称,没有考虑。当它被命名时会发生这种情况。

【问题讨论】:

  • 不要调用你的变量return
  • 我已经修改了原始问题,发布问题时我不假思索地修改了变量名。无论名称如何,都会发生这种情况

标签: javascript node.js redis socket.io


【解决方案1】:

那些 redis 调用是异步的。这就是你为他们提供回调的原因。即使您出于这个原因修复了变量名,代码也不会工作。

详细说明:当数据可用时,将调用“hkeys”回调中的代码。但是,该调用将立即返回,因此此时您的数组中将没有任何内容。

您不能将异步调用包装在一个函数中并期望返回一个值。它根本行不通。

相反,一般模式是完全按照 redis API(以及 node.js 世界中的几乎所有其他东西;事实上这就是重点):给你自己的函数一个回调参数,以便在何时调用合适的。在您的情况下,它将在最后一个被调用的“hgetall”回调中。它应该知道结果数组中的值与键的数量一样多,因此是时候调用传入函数的回调了。

(我应该注意到,鉴于整个函数似乎是对某事的回调,目前尚不清楚您要做什么。)

另一种方法是使用某种“承诺”模式,尽管这实际上只是对同一想法的重组。

edit — 带有回调的 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 );
                    }
                });

        });

【讨论】:

  • 你会如何建议做上述?
  • @grsx 我更新了答案。我不确定您要做什么。换句话说,你的问题显然是你的测试结果,以确定发生了什么,但不清楚你最终会用“returnArr”做什么。
  • 基本上,我需要从redis商店返回客户端请求的项目列表。完成后,我需要将所有结果作为数组中的对象返回。然后将该数组传递给客户端以执行任何操作。
  • 好吧,你不能那样做 :-) 但是你可以做的是让客户端提供一个函数,以便在所有结果收集完毕后调用.
  • 你能举个简单的例子吗?
【解决方案2】:

你不能调用你的变量return

它是少数几个不能在代码中用作变量的保留字之一。

【讨论】:

    【解决方案3】:

    正如 Neal 建议的,不要对变量使用 javascript 保留字,这里是列表:

    https://developer.mozilla.org/en/JavaScript/Reference/Reserved_Words

    【讨论】:

      【解决方案4】:

      @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 行:为“测试”事件注册事件处理程序。

      第 16 行:开始运行在此通过事件循环期间要运行的任何其他代码

      第 2 行:事件循环在某个时间点接收到“测试”事件并正在处理,因此 returnArr 已初始化

      第 3 行:执行非阻塞 IO 请求,并注册回调函数以在适当的事件排队进入事件循环时执行。

      第 14-15 行:lastconsole.log 被执行,该函数运行完毕,应该结束当前正在处理的事件。

      第 4 行:请求事件返回并执行回调。 forEach 方法是少数具有回调的阻塞 Node.js 方法之一,因此每个回调都会在每次回复时执行。

      第 5 行:if 语句被执行并结束(转到第 10 行)或进入块(转到第 6 行)

      第 6 行:执行非阻塞 IO 请求,将新事件添加到事件循环中,并在事件返回时运行新的回调。

      第 9 行:完成回调的注册。

      第 10 行:完成 if 语句

      第 11 行:完成 `forEach 回调。

      第 12 行:执行 second console.log 请求,returnArr 中仍然没有任何内容

      第 7 行:其中一个事件返回并触发事件处理程序。 returnArr 获得了新数据。

      第 8 行:first console.log 被执行。根据这是哪个事件,数组的长度会有所不同。此外,数组元素的顺序 DOES NOT 必须与 replies 数组中列出的回复的顺序相匹配。

      从本质上讲,您可以将嵌套更深的函数视为在执行 整个嵌套较浅的函数之后(因为本质上就是这样),而不管方法是否包含之后的语句是否嵌套非阻塞回调。

      如果这让您感到困惑,您可以在Continuation Passing Style 中编写回调代码,这样很明显外部函数中的所有内容都在内部函数之前执行,或者您可以使用this nice async library 使您的代码看起来更势在必行。

      我认为,这回答了您真正的问题,而不是您输入的问题。

      【讨论】:

      • 感谢您的详细解释。非常感谢。它确实回答了为什么变量为空,但我已将 Pointy's 标记为答案,因为它包含一个解决方案。如果可以的话,我会同时标记或投票给你,但我需要 15 个代表才能投票。
      猜你喜欢
      • 2011-06-23
      • 2015-09-07
      • 2017-12-10
      • 2023-03-27
      • 2012-06-11
      • 2015-02-23
      • 2012-09-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多