【问题标题】:recursive javascript function with a parameter?带有参数的递归javascript函数?
【发布时间】:2013-04-29 16:10:23
【问题描述】:

我得到了这个 javascript 递归函数:

function doStuff(graphName) {
    var groupArray = new Array();
    groupArray[0] = "hour";
    groupArray[1] = "day";
    groupArray[2] = "month";
    for(var i = 0; i < groupArray.length; i++) {
        $.get("getchartdata", {"graphName" : graphName, "subgroup" : groupArray[i]})
              .done(function(jsonData){
                  var data = eval(jsonData);
                  drawChart(data, data[0][0], data[0][1]);
              });

    }
    setTimeout(doStuff, 10000);
}

现在的问题是它第一次运行良好,但10秒后再次尝试,它显示错误:

TypeError: data[0] is undefined in drawChart(data, data[0][0], data[0][1]);

为什么会发生这种情况?

如果我在setTimeout(doStuff(graphName), 10000);中添加参数

浏览器崩溃。

谢谢。

【问题讨论】:

  • 永远不要在那里使用eval! (不适用于 JSON 解析,在已经解析时也不适用)
  • 它不是递归函数。
  • 请记住 $.get 是异步的,因此如果调用完成时间超过 10 秒,则在所有调用完成之前调用 doSomething 可能会导致问题。您可能想要使用 $.ajax 并设置 async = false。
  • @rontornambe 不,永远永远使用async: false
  • 虽然我不相信硬性规定,但在这种情况下,我同意异步是一个坏主意。更好的解决方案是包含一个计数器 should 并且在达到 groupArray.length 之前不调用 doSOthing。

标签: javascript function recursion


【解决方案1】:

我想你想要的是这样的:

setTimeout(function() { 
    doStuff(graphName); 
}, 10000);

还值得注意的是,如果您的 AJAX 调用需要超过 10 秒才能完成,您可能会开始看到一些“故障”。也许考虑将超时移动到 .done 回调内部(这意味着它在 ajax 完成后 10 秒 再次运行)。但是,这只是一个建议,如果这不适合您的需求,那么您可以保持原样。此外,这可能不适合,因为您在 for 循环中调用 ajax,如果您没有正确实现,您最终可能会遇到比您想要的更多的超时

【讨论】:

  • 非常感谢您的回复!既然你是第一个发帖的,那我就给你打勾! :)
【解决方案2】:

如果你想传递graphname 参数,你需要明确地这样做!好像你想要的

function doStuff(graphName) {
    var groupArray = ["hour", "day", "month"];
    for(var i = 0; i < groupArray.length; i++) {
        $.get("getchartdata", {"graphName" : graphName, "subgroup" : groupArray[i]})
              .done(function(data){
                  drawChart(data, data[0][0], data[0][1]);
              });
    }
    setTimeout(function() {
        doStuff(graphName); // again
    }, 10000);
}

另一种可能性是 bind 为下一次 doStuff 调用提供参数:

setTimeout(doStuff.bind(this, graphName), 10000);

【讨论】:

  • 谢谢!这是正确的答案,但是musefan首先回答了!我也想给你打勾:'(你很有帮助!
  • @Alnitak 让我看看你的。
  • @Bergi 不,有一种非常好的方法可以做到这一点,而无需重复传递相同的参数(或使用.bind
  • @Alnitak:不确定额外的封闭是否更干净。取决于参数的数量和可变性以及我想说的个人风格......
  • 我经常使用这种模式进行伪递归。它还允许您很好地维护循环计数器和东西,而不会受到循环/闭包范围问题的影响。
【解决方案3】:

在这种情况下,通常在内部函数中进行艰苦的工作,最初提供的参数可通过闭包获得:

function doStuff(graphName) {
    (function loop() {
         // draw the graph using "graphName" from the outer scope
         ...
         setTimeout(loop, 10000);
    })();  // invoke immediately to start the process
}

使用闭包避免了一遍又一遍地传递参数的重复,以及围绕该调用的附加函数包装,因为您只需将引用传递给内部函数。

这也适用于 AJAX - 只需将 setTimeout 调用放在 .done 处理程序中。当您进行三个 AJAX 调用时,请在内部函数中尝试此操作,该函数将在启动计时器之前等待所有三个 AJAX 调用完成:

var def = [];

for (var i = 0; i < groupArray.length; i++) {
    def[i] = $.get("getchartdata", {"graphName" : graphName, "subgroup" : groupArray[i]})
          .done(function(data) {
              drawChart(data, data[0][0], data[0][1]);
          });
}

// wait for all three deferred objects to be resolved
$.when.apply($, def).done(function() { setTimeout(loop, 10000) });

【讨论】:

  • 将超时设置到 ajax 回调中有点复杂,因为他有 3 个并行运行 :-) 10 秒应该没问题,他可以使用 timeout 参数来避免故障……
  • @Bergi nah,这对$.when 电话来说是微不足道的。
  • $.when.apply($, ["hour", "day", "month"].map(function(g) { return $.get("getchartdata", {"graphName":graphName,"subgroup":g}).done(function(d){ drawChart(d, d[0][0], d[0][1]);}); })).done(setTimeout.bind(window, doStuff.bind(this, graphName), 10000)); 对我们来说可能是微不足道的,但对初学者来说却不是 :-)
  • @Bergi 好吧,如果你坚持要用这两个 .bind 电话来复杂化它...
猜你喜欢
  • 2015-12-05
  • 2014-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-15
  • 2017-03-15
  • 2019-05-08
  • 1970-01-01
相关资源
最近更新 更多