2012-03-22 83 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