2015-05-25 157 views
1

我已經從多個地方讀取setTimeout()比setInterval()更適合設置基本上永遠運行。下面的代碼工作正常,但運行Firefox(38.0.1)大約一小時後會拋出too much recursion的錯誤。jquery setTimeout太多遞歸

本質上,我已經從counts.php中抓取了很少量的文本,並使用該信息更新了一張表。根據檢查人員的整個呼叫和回報大約需要50毫秒。我試圖按照t的指示每隔x秒做一次。我猜想如果我切換到setInterval()這可能會工作,但我不確定setTimeout()vs setInterval()心態的當前狀態是什麼,因爲我一直在找的所有東西都是關於3- 5歲。

$(document).ready(function() { 
    t = 3000; 
    $.ajaxSetup({cache: false}); 

    function countsTimer(t) { 
     setTimeout(function() { 
      $.getJSON("counts.php", function (r) { 
       $(".count").each(function(i,v) { 
        if ($(this).html() != r[i]) { 
         $(this).fadeOut(function() { 
          $(this) 
           .css("color", ($(this).html() < r[i]) ? "green" : "red") 
           .html(r[i]) 
           .fadeIn() 
           .animate({color: '#585858'}, 10000); 
         }) 
        }; 
       }); 

       t = $(".selected").html().slice(0,-1) * ($(".selected").html().slice(-1) == "s" ? 1000 : 60000); 

       countsTimer(t); 
      }); 
     }, t); 
    }; 
    countsTimer(t); 
}); 

更新:此問題是由.fadeOut()動畫之前添加.stop(真正的,真實的)解決。此問題僅在Firefox中發生,因爲其他瀏覽器中的測試不會導致任何問題。儘管在這種情況下它不是解決方案,但我已經將答案標記爲正確,但它提供了一個更廣義的解釋。

+1

我不知道爲什麼'setInterval'不應該被使用。原因是什麼?聽起來像虛假的聲明或僅在特定情況下相關的原因。 –

+0

林想知道是否清除超時將解決這個問題,即使我真的不知道從哪裏來遞歸 –

+0

@squint,以避免一些問題,如果請求超過延遲完成 –

回答

1

在這種情況下,您確實應該切換到setInterval()。 setInterval()的問題在於,如果您想要清除超時並且萬一操作(可能)執行時間超過超時本身,操作可能會運行兩次,則必須保留引用。

例如,如果您有一個函數每隔1s使用setInterval運行,但由於緩慢的XHR請求,函數本身需要2s才能完成,因此該函數將在某個時刻同時運行兩次。這通常是不可取的。通過使用setTimout並在原始函數結束時調用該函數,該函數從不重疊,並且您設置的超時始終是兩次函數調用之間的時間。

但是,在你的情況下,你似乎有一個長期運行的應用程序,因爲你的函數每3秒運行一次,函數調用堆棧會每三秒增加一個。除非你打破這個遞歸循環,否則這是無法避免的。例如,您只能在接收瀏覽器事件(如單擊文檔並檢查時間)時執行請求。

(function() 
{ 
    var lastCheck = Date.now(), alreadyRunning = false; 
    document.addEventListener 
    (
     "click", 
     function() 
     { 
      if(!alreadyRunning && Date.now() - lastCheck > 3000) 
      { 
       alreadyRunning = true; 
       /* Do your request here! */ 
       //Code below should run after your request has finished 
       lastCheck = Date.now(); 
       alreadyRunning = false; 
      } 
     } 
    ) 
}()); 

這沒有缺點的setInterval呢,因爲你經常檢查代碼是否已經在運行,但是支票接收瀏覽器事件時,只運行。 (這通常不是問題。)而這種方法會產生更多的樣板。

因此,如果您確定XHR請求不會超過3秒才能完成,請使用setInterval()。

編輯:回答以上是錯誤的在某些方面

正如在評論中指出,setTimeout的()確實不增加調用堆棧大小,因爲它在超時的函數調用之前返回。此外,問題中的函數不包含任何特定的遞歸。我會保留這個答案,因爲部分問題是關於setTimeout()和setInterval()的。但是,導致遞歸錯誤的問題可能會出現在其他代碼片段中,因爲沒有函數直接或間接地在示例代碼中的任何位置調用它自己。

+0

我應該補充說,使用頁面就是它將被展示並且從未真正與之互動過,所以它可以在那裏呆上好幾個小時,而鼠標不會移動不使用setInterval的唯一原因是它可能重疊並已經運行實例?正如你在這裏使用的那樣,爲「alreadyRunning」添加一個支票幾乎可以緩解這個問題? – mbazdell

+0

在這種情況下,這是一個正確使用setInterval()的教科書示例。 – w3re

+0

如果應該使用'setTimeout'或'setInverval',那麼與函數調用堆棧增加部分的回答不正確無關。 'countTimer'在setTimeout中的函數執行下一個'countsTimer'調用之前返回並從堆棧中移除。至少它應該。 – nomve