因為自己在平時工作中,有些功能需要用到定時器,但是定時器並不像我們表邊上看到的那樣,所以這周末我看看書查查資料,深入研究了一下JavaScript中的定時器,那麼廢話不多說,下麵進入我們今天的正題。 大家都知道JavaScript是單線程的,所以不管是定時器還是用戶的操作都是需要線上程隊列中排隊執行 ...
因為自己在平時工作中,有些功能需要用到定時器,但是定時器並不像我們表邊上看到的那樣,所以這周末我看看書查查資料,深入研究了一下JavaScript中的定時器,那麼廢話不多說,下麵進入我們今天的正題。
大家都知道JavaScript是單線程的,所以不管是定時器還是用戶的操作都是需要線上程隊列中排隊執行的。
一、定時器在執行線程隊列里的分析
為了更好的理解我還是直接寫個測試代碼來看一下,這樣分析起來更直觀一些
1 <script type="text/javascript"> 2 var time1 = new Date().getTime(); 3 console.log("階乘函數開始時間:"+time1); 4 function factorial(num) { 5 if(num <= 1) { 6 return 1; 7 } else { 8 return num * arguments.callee(num - 1); 9 } 10 } 11 var result = factorial(150); 12 console.log("階乘函數結果:"+result); 13 var time2 = new Date().getTime(); 14 console.log("階乘函數結束時間1:"+time2); 15 console.log("階乘函數所用時間:"+(time2-time1)); 16 setTimeout(function(){ 17 var time3 = new Date().getTime(); 18 factorial(10000); 19 var time4 = new Date().getTime(); 20 console.log("setTimeout被執行時間:"+time3); 21 console.log("setTimeout執行的差值時間:"+(time3-time1)); 22 console.log("setTimeout裡面的函數運行時間:"+(time4-time3)); 23 },10); 24 setInterval(function(){ 25 var time5 = new Date().getTime(); 26 console.log("setInterval被執行時間:"+time5); 27 console.log("setInterval執行的差值時間:"+(time5-time1)); 28 },10); 29 </script>
在上面這段測試代碼裡面一共分為三個部分:
1.階乘計算
2.setTimeout
3.setInterval
一般人們的認知是先開始執行階乘計算,然後10ms之後會運行setTimeout裡面的內容,並且每個10ms會觸發setInterval。錶面上看是這樣,但是事實並不一定是這樣的。
下麵我貼出代碼的運行結果來逐一的分析
從運行結果可以看出,階乘函數運行需要花費15ms(多次刷新頁面可能這個時間會不一樣,所以我們先姑且認為這個階乘函數執行需要15ms)。
最開始的時候會啟動一個10ms延遲的setTimeout定時器和一個10ms間隔的setInterval定時器,由於第一階段的階乘函數需要花15ms,所以在10ms的時候,前面兩個定時器都已經過期了,由於JavaScript的單線程,這兩個定時器就需要進入線程隊列進行等待,直到第一段階乘函數運行完。
所以可以看到setTimeout在15ms的時候才開始執行,這就是我要說的定時器即使指定了時間不一定就能決定函數何時被執行。通過列印的結果可以看到setTimeout執行完需要5ms,所以在20ms的時候setInterval被開始執行。
我這裡講的是三段函數線上程隊列里的開始被執行的時間,而不是函數最後執行完的時間。
二、setTimeout和setInterval之間的區別
還是直接上代碼來進行對比
1 setTimeout(function cb(){ 2 console.log("setTimeout"); 3 setTimeout(cb,10); 4 },10); 5 setInterval(function(){ 6 console.log("setInterval"); 7 },10);
上面的代碼看上去功能似乎是一樣的,實際上兩者是有區別的,在setTimeout里要等裡面的函數執行完(也就是前一個callback)再延遲10ms才可以再次執行回調函數,而setInterval是每隔10ms就會去執行函數裡面的內容,而不去管上一個是否執行完。這就是兩者之間的區別。
三、定時器延遲時間的可靠性
看了我第一部分貼出的運行結果之後,細心的人可能會發現我列印的定時器延遲會有1-2ms的偏差,就是下麵我要看的延遲時間可靠性的問題了
廢話不多說直接上測試代碼(為了測試效果這裡我極端一點把延遲時間設置為1ms,這樣數據結果會更加明顯一點)
1 var time1 = new Date().getTime(); 2 setInterval(function(){ 3 var time2 = new Date().getTime(); 4 console.log("setInterval執行的差值時間:"+(time2-time1)); 5 },1);
常理上來講運行的結果應該是1 2 3 4 5 6 8 9 ...
但是實際的運行結果如下:
可以看到平均的延遲時間大概在5ms左右,我是拿chrome瀏覽器測試的,據說不同的系統下不同的瀏覽器這個平均的時間都不一樣,如果愛鑽牛角尖的小伙伴可以去嘗試一下,這裡就不深入展開了.
所以說定時器的延遲時間不宜設置過小,因為太小的話可能根本也達不到你想要的效果(不排除一些真可以控制在1ms左右的牛逼瀏覽器),而且根據設備硬體或者瀏覽器的不同可能延遲時間也會有少量的誤差
四、定時器的小妙用
有人會說定時器能有什麼妙用,無外乎就是在手機翻轉屏的時候延遲幾秒獲取設備寬度等等。
我想說的不是這些而是利用定時器來提高性能的辦法。
首先我們來模擬一個js要動態創建十萬個DOM節點的場景,這種情況瀏覽器會花費大量的時間來執行,從而阻塞了其他代碼的執行。這時如果我們使用定時器把這十萬個DOM打散分成多個部分,這樣一下就好了很多。
五、合理管理定時器
大家都知道定時器,用完之後需要清除,如果不清楚同時多個定時器在一個頁面上跑,會損耗性能讓頁面瀏覽起來有卡頓感,尤其是定時器在動畫裡面的應用,想象一下,如果製作一個動畫效果裡面用了許多定時器,並且多個定時器同時運行,那麼有可能就會出現本來後面要執行的一個動畫效果在前面就被提前執行了,這是我們不想看到的。
所以我們就要根據情況對定時器做一個合理的管理,還是拿做動畫為例,
1.動畫肯定要保持同一時間只執行一個定時器;
2.並且自己可以靈活的控制定時器的開啟和關閉。
在網上可以找到管理定時器的示例代碼,大家可以參考一下:
1 var timers = { 2 timerID : 0, 3 timers : [], 4 add : function (fn) { 5 this.timers.push(fn); 6 }, 7 start : function (){ 8 if(this.timerID) return; 9 (function runNext(){ 10 if(timers.timers.length>0){ 11 for(var i=0;i<timers.timers.length;i++){ 12 if(timers.timers[i]() === false){ 13 timers.timers.splice(i,1); 14 i--; 15 } 16 } 17 timers.timerID = setTimeout(runNext,10); 18 } 19 })(); 20 }, 21 stop : function(){ 22 clearTimeout(this.timerID); 23 this.timerID = 0; 24 } 25 };View Code
其實管理的核心還是我上面提到的兩條。
總結
1.如果無法立即執行定時器,定時器會進入線程隊列當中排隊,等待下一個可執行的時間點,所以說定時器有些時候可能要比設定的時間要長,但是不會比設定的時間少的;
2. setTimeout和setInterval在被觸發的定義上是有很大區別的;
3.定時器的延遲時間不宜設置過小;
4.利用定時器可以分解大量操作的代碼
5.合理管理頁面中的定時器
祝大家清明小長假快樂!(其實也就比平時周末多一天而已)
感謝大家的觀看,有什麼分析的不對的地方歡迎大家批評指出,如果喜歡本文,請點擊右下角的推薦哦~