定時器(setTimeout)的祕密

美美噠武功山

原文地址:→傳送門javascript

寫在前面

setTimeout()是你們再熟悉不過的定時器,但平時對定時器的瞭解甚少,因而想看看setTimeout()的原理機制。html

setTimeout()基礎

setTimeout()函數用來指定某個函數或某段代碼,在多少毫秒以後執行。它返回一個整數,表示定時器的編號,之後能夠用來取消這個定時器。java

var timeer = setTimeout(function|code,delay);

注:其中爲code時須要以字符串形式傳入,通常都是使用傳入function的形式。chrome

setTimeout(func,delay,arg1,arg2···)瀏覽器

setTimeout()可傳入多個參數,第三個參數起是做爲回調函數的參數傳入。函數

function add(a,b){
            console.log(a+b);
        }
        setTimeout(add,4000,20,30);  //20和30做爲add的參數傳入 結果爲50

HTML5規定setTimeout()的最短期間隔是4ms,對於不處在當前窗口的頁面,瀏覽器會將時間間隔擴大到1000ms,若筆記本電腦處於電池供電狀態,chrome及IE9以上版本,會將時間間隔切換到系統定時器,約15.6ms。oop

setTimeout()中回調函數的指向

setTimeout()回調函數調用的是某個對象的方法,則其中this指向的是setTimeout所處的環境而並不是指向該對象環境。性能

可以使用ES5中的bind()方法解決this指向,即將對象環境綁定到要執行的回調函數上便可。this

var name = 'lily';
        function Person(name){
            this.name = name;
            this.printName = function(){
                console.log(this.name);
            }
        }

        var person01 = new Person('jike');
        
         //此處的this.name爲window下的name,因此是'lily'
        window.setTimeout(person01.printName,1000); 
        
        //此處將printName綁定在person01上,因此this.name的值爲'jike'
        window.setTimeout(person01.printName.bind(person01),2000);  
        
        //將printName中的this.name與person01限定在同一個做用域中,因此結果是'jike'
        window.setTimeout(function(){person01.printName();},3000); 
        
        //藉助ES6中的箭頭函數也能夠解決問題
        window.setTimeout(() => {person01.printName},1000);

setTimeout運行機制

setTimeout()和setInterval()的運行機制是,將指定的代碼移出本次執行,等到下一輪Event Loop時,再檢查是否到了指定時間。若是到了,就執行對應的代碼;若是不到,就等到再下一輪Event Loop時從新判斷。這意味着,setTimeout()指定的代碼,必須等到本次執行的全部代碼都執行完,纔會執行。code

例如:

task1();
setTimeout(otherTask,1000);
task2();

其中task1和task2爲當即執行任務,otherTask被指定在1s後執行,此時otherTask被添加到任務隊列的尾部,要等當前的腳本中Event Loop的任務隊列所有執行完成後,纔開始執行otherTask,但若是task1和task2很耗時,前面的任務超過1s還未結束,此時otherTask只能等task1和task2運行結束,纔會執行otherTask。

setTimeout(func,0)的運用

當延遲時間設爲0時,即當前任務隊列一結束就當即執行func。(換個角度說就是同步任務的任務隊列結束後儘早執行。)

task01();
        setTimeout(function(){
            console.log('hello!');  //在task01和task02結束後當即打印hello!
        },0)
        task02();

0ms在其實是達不到的,根據HTML5標準,setTimeout()推遲執行的時間最少是4ms,若是小於這個值,會被自動增長到4,同時也是爲了防止多個setTimeout(func,0)語句連續執行,形成性能問題。

  • setTimeout(func,0)能夠調整事件的發生順序。

clearTimeout()

setTimeout()和setInterval()函數,都返回一個表示計數器編號的整數值,將該整數傳入clearTimeout()和clearInterval()函數,就能夠取消對應的定時器。兩種定時器用的同一個編號池。

setTimeout()和setInterval()函數返回的整數值都是連續的,所以能夠利用循環來清除全部的定時器。

防抖動(debounce)

該方法用於防止某個函數在短期內被密集調用,具體來具體來講,debounce方法返回一個新版的該函數,這個新版函數調用後,只有在指定時間內沒有新的調用,纔會執行,不然就從新計時。

實際中不要設置太多個setTimeout()和setInterval(),它們耗費CPU,理想作法是將要延遲執行的代碼都放在同一個函數裏,而後支隊這個函數使用setTimeout()和setInterval()。

setInterval()

setInterval()使用原理機制與setTimeout()同樣,區別就是setInterval()是每隔多少ms執行一次回調函數。也能夠傳入大於兩個參數。

setInterval()指定的是「開始執行」之間的間隔,並不考慮每次任指定的是「開始執行」之間的間隔,並不考慮每次任,好比,setInterval()指定每100ms執行一次,每次執行須要5ms,那麼第一次執行後95ms後,第二次執行就會開始。若是某次執行須要105ms,即超過了delay時間,則執行結束後下一次執行就會當即開始。

setInterval()的最短間隔是10ms,小於10ms的時間間隔會被調整到10ms

setInterval()具備累積效應,若是某個操做特別耗時,超過了setInterval()的時間間隔,排在後面的操做就會被累積起來,而後在很短的時間內連續觸發,這可能會形成性能問題。

資源參考

相關文章
相關標籤/搜索