對於「不用setInterval,用setTimeout」的理解

JavaScript高級程序設計(第三版)(如下簡稱紅寶書)22.3高級定時器中詳細介紹了定時器setTimeout和setInterval,看完書後,深刻理解了兩者的區別,結合前輩們給個人建議「用setTimeout,不要用setInterval」,寫下此文,分析這個建議的合理性。
這兩個傢伙看上去長得差很少,func是要執行的函數,interval是時間間隔。函數

setTimeout(func,interval)
setInterval(func,interval)

關於時間間隔,紅寶書中這麼說:spa

設定一個 150ms 後執行的定時器不表明到了 150ms 代碼就馬上執行,它表示代碼會在 150ms 後被加入到隊列中。若是在這個時間點上,隊列中沒有其餘東西,那麼這段代碼就會被執行。

對於這個時間間隔的理解很是重要!步入正題,爲什麼不用setInterval,由於它可能會帶來兩個問題:線程

  • 「丟幀」現象
  • 不一樣定時器的代碼的執行間隔比預期小

一圖勝千言,以下圖所示,讓咱們跟着時間線看看這樣的問題怎麼發生的。假定一個場景,在click事件中設置了setInterval(func,500),假設click事件和定時器內函數的執行時間都是1s,爲了方便陳述,我把不一樣時間觸發的func取了不一樣的名字,實際上接下來的func1=func2=func3=func。在0s處觸發click事件,click事件執行,在0.2s處觸發定時器,0.7s處第一個函數func1加入到事件隊列,但因爲JS引擎是單線程的,click事件還在執行,因此func1等待着,等到1s處,click事件執行完畢,fun1纔開始執行。按照定時器的時間間隔,1.2s處第二個函數func2加入到事件隊列,但此時fun1正在執行,因此func2只能等待。0.5s後,也就是1.7s處,第三個函數func理應加入事件隊列,可是JS引擎作了一個事情:設計

當使用 set Interval()時,僅當沒有該定時器的任何其餘代碼實例時,纔將定時器代碼添加到隊列中。

在1.7s處,func1在執行,func2在隊列裏等待執行,func2就是該定時器的代碼實例,按照JS引擎的處理,func3不會加入到事件隊列裏,更別說執行了,這就致使出現了「丟幀」現象。而在圖中也能夠注意到,func1執行完畢,線程空閒了,func2就能夠執行了,這就使得func1和func2之間的執行沒有時間間隔,這跟咱們所預期的500ms產生一次結果是不一樣的。code

setInterval遇到的問題

而若是使用鏈式setTimeout調用,每次函數執行的時候都會建立一個新的定時器。第二個setTimeout()調用使用了 arguments.callee 來獲取對當前執行的函數的引用,併爲其設置另一個定時器。這樣作的好處是,在前一個定時器代碼執行完以前,不會向隊列插入新的定時器代碼,確保不會有任何缺失的間隔。並且,它能夠保證在下一次定時器代碼執行以前,至少要等待指定的間隔,避免了連續的運行。代碼以下所示blog

setTimeout(function(){
    //do something
    setTimeout(arguments.callee,interval);
},interval)

用setTimeout方法的話,上面假設的場景就發生了改變,以下圖所示,在0s處觸發click事件,click事件執行,在0.2s處觸發定時器,0.7s處第一個函數func1加入到事件隊列,click事件執行了1s,在1s處func1執行,2s處func1執行結束,第二個setTimeout定時器才被觸發,0.5s後將函數func2加入隊列,此時隊列爲空,func2開始執行,3.5s處func2執行結束,又一個setTimeout定時器被觸發,0.5s後將函數func3加入隊列,此時隊列爲空,func3開始執行。。。隊列

setTimeout

經過上面這個場景,咱們能知道當須要用定時器來設置一個操做重複執行,而且這個操做須要執行必定的時間,記得用setTimeout,不用setInterval!事件

相關文章
相關標籤/搜索