2012-03-22 82 views
9

我有一个事件,它可以自我激发。我尽量使代码尽可能高效,但在某些情况下它可能达到最大调用堆栈,这是我无法控制的。这不是一个无限的堆栈,它会在某个时候结束,但有时它可能会因为限制而结束之前发生崩溃。如何增加Javascript中的最大调用堆栈?

我会增加调用堆栈的数量,如果我设置了2个类似的事件侦听器和分裂的代码?或者我能做什么?

UPDATE:它是基于DOM变化事件(只使用Webkit,所以不关心其他浏览器),它也可以根据某些条件修改DOM。我还没有真正达到这个限度,但在理论上,它可能会。我仍在优化代码,尽可能减少DOM操作。

更新2:我包括样品(不实)例如:

document.addEventListener('DOMSubtreeModified', function(event){ 

    this.applyPolicy(event); 

}, true); 

function applyPolicy(event){ 
    if(typeof event != "undefined"){ 
     event.stopPropagation(); 
     event.stopImmediatePropagation(); 
    } 

    if(!isButtonAllowed){ 
     $('button:not(:disabled)').each(function(){ 

      $(this).attr('disabled', true); 

     }); 
    } 
} 

这仅仅是一个示例代码,但即使在这种情况下,如果你有说按钮的100S,呼叫堆栈也将在100秒内。请注意,如果您使用$('button').attr('disabled', true);,这将导致调用堆栈问题,因为jQuery将试图无限修改DOM。

+1

也许你应该把你的递归函数转换成while循环吗?我无法想象你需要实际数百个事件触发器的副作用来触发其他功能...... – 2012-03-22 18:29:43

+1

从来没有达到过这个限制,如果不是在错误地编码一个无限循环时。显示代码.. – gpasci 2012-03-22 18:30:05

+2

简答:没有(标准)机制来执行此操作。唯一的选择是改变代码。 – 2012-03-22 18:30:42

回答

6

虽然这听起来像你可能需要重新考虑一些代码,一个可能性是放在一个setTimeout递归调用在某个给定的时间间隔。这使您可以开始新的调用堆栈。

拿这个例子...

var i = 0; 

function start() { 
    ++i; 
    var is_thousand = !(i % 1000); 

    if (is_thousand) 
     console.log(i); 

    if (i >= 100000) 
     return; // safety halt at 100,000 
    else 
     start() 
} 

它只是记录到的1,000每间隔控制台。在Chrome中,它超出了30,000范围内的堆栈。

DEMO:http://jsfiddle.net/X44rk/


但如果返工这样的...

var i = 0; 

function start() { 
    ++i; 
    var is_thousand = !(i % 1000); 

    if (is_thousand) 
     console.log(i); 

    if (i >= 100000) // safety halt at 100,000 
     return; 
    else if (is_thousand) 
     setTimeout(start, 0); 
    else 
     start(); 
} 
1,000

现在,该函数将被允许返回和下一调用将异步开始,启动一个新的调用堆栈。

请注意,这是假设函数在递归调用时有效结束。

另外请注意,我有一个条件在100,000停止,所以我们不是无限的。

DEMO:http://jsfiddle.net/X44rk/1/

+0

这看起来很有趣。我会给它一个镜头。谢谢! – Sherzod 2012-03-22 18:52:33

+0

@shershams:不客气。如果您有任何问题,请告诉我。 – 2012-03-22 18:54:29

+1

我用百万次递归尝试了你的代码,它仍然正常工作。我正在使用这种技术来实现我的代码。谢谢! – Sherzod 2012-03-22 19:06:22

6

对于任何浏览器的最大调用堆栈在千运行良好。你应该尝试优化代码,拥有巨大的调用堆栈不利于提高速度和内存。

如果你正在运行到这一点,这是一个指标你的代码是急需重组

+0

事情是,该事件正在侦听DOM更改,并且在检查了一些条件之后,它实际上可以修改DOM,从而再次触发该事件。因此,调用堆栈可能会增加很快。我仍在努力优化代码,尽可能减少DOM操作。 – Sherzod 2012-03-22 18:31:58

+0

嗯,这是我知道的一个非常一般的评论..但是你需要更清楚地区分顾虑。如果你听domchange事件,这意味着你依靠你的DOM作为'模型'或'视图模型'。这有很多错误。尝试实施backbone.js,即使你不喜欢那个具体..你可以从良好的UI设计模式了解很多。 – Evert 2012-03-22 18:37:16

+0

我正在使用JavascriptMVC和jQuery。在这种情况下减少递归的唯一方法是减少DOM操作的数量,以免它经常自我激发..有更好的方法吗? – Sherzod 2012-03-22 18:41:09

0

的调用堆栈和实施细则的大小将取决于你的代码在运行JavaScript环境。即使你可以操纵堆栈使用以在您现在关心的环境中运行,但是在提供不同堆栈大小的环境中运行时,堆栈饥饿的代码很有可能会崩溃。

任何递归程序可以改写为一个迭代。考虑一下,如果你可以在你的情况下做到这一点。请参阅:

Way to go from recursion to iteration

0

不能,他们是依赖于浏览器和很坦率地说,他们有相当广泛的,所以没有必要IMO担心。

0

如果你打的调用堆栈的限制,你几乎可以肯定有一个递归一系列事件。事件依赖于堆栈,所以它们实际上不是实现循环的最佳方式。在没有尾部调用消除的语言中,唯一真正的选择是使用像/ in这样的标准循环结构。

0

找出你正在审查可能使你不希望渲染的元素第三方代码后,我明白了真正的根本问题:黑名单从未在不受信任的代码上工作,最终有些代码会越过您的黑名单,您就完成了。

重新构建你的代码,没有第三方的代码期望(或给予)访问DOM之外,我的解决办法如下:

  1. 复制你的DOM到隐藏的iframe。
  2. 沙箱在所述iframe中的第三方代码。
  3. 在iframe中的任何DOM更改中,检查iframe是否存在两个DOM树之间的差异。如果更改通过白名单,请将其拉​​到您的真实DOM中(重新附加事件回调等)。
  4. 将更新后的“真实”DOM的结构复制到iframe DOM中,在该过程中从其擦除任何敏感数据。

您仍然会检查iframe中的DOM事件,但是您不会在“实际”页面中查看,因此无法输入无限循环。

这是假设你真的不能信任你的第三方代码做它应该做的。如果这只是供应商无能,忘记清理部分,但坚持白名单,而不是黑名单,反正。

+0

代码本身在发布之前由不同的脚本进行分析,但第三方开发人员可能忘记在某些事件中禁用某些事项,因此我需要检查它并执行该策略。我一定会尝试使用你提到的方法创建一些非常简单的东西。尽管我讨厌它们,但内联框架的想法听起来很有趣。谢谢! – Sherzod 2012-03-22 20:55:24