如何在Javascript中增加最大调用堆栈?

时间:2012-03-22 18:23:40

标签: javascript jquery dom javascript-events

我有一个可以解雇的事件。我尝试使代码尽可能高效,但在某些情况下它可以达到最大调用堆栈,这是我无法控制的。它不是一个无限的堆栈,它会在某个时刻结束,但有时它可能会因为极限而在它完成之前崩溃。

如果我设置了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);

        });
    }
}

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

6 个答案:

答案 0 :(得分:6)

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

如果您遇到此问题,则表明您的代码迫切需要重组

答案 1 :(得分: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/

答案 2 :(得分:0)

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

任何递归程序都可以重写为迭代程序。考虑一下你是否可以在你的情况下这样做。参见:

Way to go from recursion to iteration

答案 3 :(得分:0)

不能,他们依赖于浏览器,而且坦率地说他们有很广泛的范围,因此无需担心IMO。

答案 4 :(得分:0)

如果你达到了调用堆栈限制,你几乎肯定会有一系列递归事件。事件依赖于堆栈,因此它们实际上不是实现循环的最佳方式。在没有尾部调用消除的语言中,您唯一真正的选择是使用标准循环结构,如for / in。

答案 5 :(得分:0)

在发现您正在审查可能呈现您不想呈现的元素的第三方代码后,我了解真正的根本问题:黑名单从不处理不受信任的代码,最终有些代码会通过你的黑名单而你已经完成了。

在重新架构代码之外,没有第三方代码期望(或被授予)访问DOM,我的解决方案如下:

  1. 将您的DOM复制到隐藏的iframe中。
  2. 沙箱中所述iframe中的第三方代码。
  3. 在iframe中的任何DOM更改中,检查iframe以查找两个DOM树之间的差异。如果更改通过了白名单,请将其拉​​入真实的DOM(重新附加事件回调等等)。
  4. 将更新后的“真实”DOM结构复制到iframe DOM中,在此过程中清除其中的所有敏感数据。
  5. 您仍然会检查iframe中的DOM事件,但您不会在“实际”页面中,因此无法进入无限循环。

    这假设你真的不能信任你的第三方代码来做它应该做的事情。如果它只是供应商的无能,忘记擦洗部分,但坚持使用白名单而不是黑名单,无论如何。

相关问题