【问题标题】:A "too much recursion" error in Firefox only sometimes?仅在 Firefox 中出现“递归过多”错误?
【发布时间】:2011-07-29 21:30:11
【问题描述】:

我正在用 javascript 做一件非常简单的事情,基本上只有有时 javascript 会给我一个“递归过多”的错误。

有问题的代码:

if(pageLoad===undefined){
  var pageLoad=function(){}; 
}
var pageLoad_uniqueid_11=pageLoad;
var pageLoad=function(){ 
  pageLoad_uniqueid_11();
  pageLoad_uniqueid_12(); 
};
var pageLoad_uniqueid_12=function(){
 alert('pageLoad');
};

$(document).ready(function(){
   pageLoad();
});

(是的,我知道有更好的方法可以做到这一点。但这很难改变,尤其是因为 ASP.Net 部分回发未显示)。

无论如何,当发生太多递归错误时,它会继续发生,直到我重新启动 Firefox。当我重新启动 Firefox 时,一切都恢复正常。我该如何解决?

我也发了jsbin example

更新

好的,我已经知道如何在我们的代码中可靠地重现它,但它不适用于 jsbin 示例。如果我创建一个新选项卡并转到同一页面(有两个相同地址的选项卡)然后刷新第一个选项卡 两次 次,那么我会始终收到此错误。我们没有使用任何类型的会话或任何我能想到的可能导致此类问题仅发生在一个选项卡中的东西!

更新 2 不像我想象的那么可靠,但它肯定只在同一页面的多个标签打开时才会发生。每隔几次重新加载打开的选项卡之一就会发生这种情况

我还更新了我的代码,以在 pageLoad(if 语句)最初未定义和最初定义时显示警报。不知何故,两个警报都出现了。此代码不会在呈现的页面中重复,并且不可能被调用两次。它位于顶层脚本元素中,没有被函数或任何东西包围!我的代码最终看起来像

if(pageLoad===undefined){ 
  var pageLoad=function(){}; 
  alert('new'); 
} else {  
  alert('old'); 
}

【问题讨论】:

  • 您正在链接到一个 jsbin 页面,同时承认它可能会使他们的 Firefox 崩溃? ;)
  • 尝试了 jsbin 页面并重新加载了几次并没有崩溃。我正在使用 Firefox 5.0
  • 好吧,看起来 Javascript 解释器通常有 3000 个内联调用的限制。如果不同的浏览器使用不同的解释器,那么这就是为什么您可能只在 Firefox 上看到错误的原因。您能否使用 jsbin 示例重现该错误?
  • 你有函数调用自己,或者自己的别名。我不确定我是否理解您为什么对遇到递归错误感到困惑。
  • @Alien 它不会使 firefox 崩溃,它只会在 javascript 错误日志中给出“递归过多”的错误

标签: javascript firefox recursion


【解决方案1】:

有问题的代码——本身——不应该永远导致无限递归问题——没有函数语句,所有函数对象都急切分配给变量。 (如果 pageload 首先是未定义的,它将被分配一个无操作函数,请参阅下一节。)

我怀疑有其他代码/事件触发了该行为。 可能导致的一件事是如果脚本/代码在页面生命周期内被触发两次。第二次pageload不会是undefined,会保持原来的值,如果是这个函数调用了另外两个函数,会导致无限递归。

我会建议清理方法 - 并且由并发症引起的任何问题都会消失 ;-) 期望的意图是什么?

编码愉快。

【讨论】:

  • 这段代码的作用是,对于 ASP.Net 部分回发,您只能获得一个“pageLoad”函数。 (唯一的 id 等由代码生成,每个页面都是唯一的)
  • 部分回发不会“重置”页面——因此pageload仍将被定义。第 2 节中的情况很可能会发生。确切的位置/方式需要更大的上下文。 (例如,JavaScript 代码是回发结果本身的一部分吗?)
  • +1,很好的一点是这段代码被调用了两次。 那么就会有递归。
  • @pst,这是有道理的,但这发生在没有更新面板的页面上,所以这不太有意义。
  • @Earlz 那时不知道。他们最简单的“修复”是删除定义的检查并始终分配 N-OP 功能。
【解决方案2】:

这只是其他人试图在他们的代码中寻找类似的“递归过多”错误的一些附加信息。看起来像 firefox(作为一个例子)在这个例子中在大约 6500 个堆栈帧深度处获得了太多递归:function moose(n){if(n%100 === 0)console.log(n);moose(n+1)};moose(0)。类似的示例可以看到 5000 到 7000 之间的深度。不确定决定因素是什么,但函数中的参数数量似乎大大降低了堆栈帧深度,在该深度处出现“递归过多”错误。例如,这只能达到 3100:

function moose(n,m,a,s,d,fg,g,q,w,r,t,y,u,i,d){if(n%100 === 0)console.log(n);moose(n+1)};moose(0)

如果您想解决这个问题,您可以使用 setTimeout 来安排迭代以从调度程序(重置堆栈)继续。这显然仅在您不需要从调用中返回某些内容时才有效:

function recurse(n) {
  if(n%100 === 0)
    setTimeout(function() {
      recurse(n+1)
    },0)
  else
    recurse(n+1)
}

ECMAScript 6 中的正确尾调用将解决某些情况下您确实需要从此类调用中返回某些内容的问题。在那之前,对于深度递归的情况,唯一的答案是使用迭代或我提到的setTimeout 方法。

【讨论】:

  • 至少有一些决定因素是:函数调用方法(fn() 或 fn.call() 或 fn.apply())、函数上下文、参数数量和局部变量 - 查看测试页面jsfiddle.net/9YMDF/12 最奇怪的情况:bugzilla.mozilla.org/show_bug.cgi?id=966173 - 一些适用于 Linux 的 Firefox 版本的调用堆栈大小约为 500,而我看到一个不愉快的 IE 安装,调用堆栈大小仅为 276。
【解决方案3】:

我遇到了这个错误。我的情况不同。罪魁祸首代码是这样的(这是简单的隐性连接)

while(row)
{
    string_a .= row['name'];
}

我发现 JavaScript 在第 180 次递归时抛出错误。直到 179 循环,代码运行良好。

Safaris 中的行为完全相同,只是它显示的错误是“RangeError: Maximum call stack size exceeded”。它也会在 180 递归时引发此错误。

虽然这与函数调用无关,但它可能会帮助那些陷入困境的人。

【讨论】:

    【解决方案4】:

    Afaik,如果您为 ajax 请求声明错误的参数,也会出现此错误,例如

    $.getJSON('get.php',{cmd:"1", elem:$('#elem')},function(data) { // ... }
    

    那么应该是

    elem:$('#elem').val()
    

    改为。

    【讨论】:

      【解决方案5】:

      这也会导致“递归过多”的问题:

      class account {
          constructor() {
              this.balance = 0;      // <-- property: balance
          }
      
          set balance( amount ) {    // <-- set function is the same name as the property.
              this.balance = amount; // <-- AND property: balance (unintended recursion here)
          }
      }
      
      var acc = new account();
      
      

      使用唯一的名称很重要。

      好的,为什么会这样?

      在 set 函数中,它实际上并没有将属性设置为数量,而是再次调用 set 函数,因为在 set 函数的范围内,设置属性和调用 set 函数的语法相同。

      因为在该范围内thisaccount 相同,(account OR this).balance = amount 都可以调用 set 函数或设置属性。

      解决方案是简单地以任何方式更改属性或 set 函数的名称(当然也相应地更新其余代码)。

      【讨论】:

        猜你喜欢
        • 2011-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-21
        • 2023-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多