白話debounce和throttle

遇到的問題

在開發過程當中會遇到頻率很高的事件或者連續的事件,若是不進行性能的優化,就可能會出現頁面卡頓的現象,好比:git

  1. 鼠標事件:mousemove(拖曳)/mouseover(劃過)/mouseWheel(滾屏)
  2. 鍵盤事件:keypress(基於ajax的用戶名惟一性校驗)/keyup(文本輸入檢驗、自動完成)/keydown(遊戲中的射擊)
  3. window的resize/scroll事件(DOM元素動態定位)

爲了解決這類問題,經常使用的方法就是throttle(節流)debounce(去抖)。throttle(節流)和debounce(去抖)都是用來控制某個函數在必定時間內執行多少次的解決方案,二者類似而又不一樣。github

下面就具體的看看二者的類似和區別。ajax

認識throttle和debounce

throttle和debounce的做用就是確認事件執行的方式和時機,之前老是不太清楚二者的區別,容易把兩者弄混。閉包

下面就經過兩個簡單的場景描述一下debounce和throttle,之後想到這兩個場景就不會再弄混了:app

debounce
假設你正在乘電梯上樓,當電梯門關閉以前發現有人也要乘電梯,禮貌起見,你會按下開門開關,而後等他進電梯; 
若是在電梯門關閉以前,又有人來了,你會繼續開門;
這樣一直進行下去,你可能須要等待幾分鐘,最終沒人進電梯了,纔會關閉電梯門,而後上樓。

因此debounce的做用是,當調用動做觸發一段時間後,纔會執行該動做,若在這段時間間隔內又調用此動做則將從新計算時間間隔函數

throttle
假設你正在乘電梯上樓,當電梯門關閉以前發現有人也要乘電梯,禮貌起見,你會按下開門開關,而後等他進電梯;  
可是,你是個沒耐心的人,你最多隻會等待電梯停留一分鐘;
在這一分鐘內,你會開門讓別人進來,可是過了一分鐘以後,你就會關門,讓電梯上樓。

因此throttle的做用是,預先設定一個執行週期,當調用動做的時刻大於等於執行週期則執行該動做,而後進入下一個新的時間週期性能

簡單實現

有了上面的瞭解,就能夠去實現簡單debounce和throttle了。優化

debounce實現

首先來看看debounce的實現,根據前面對debounce的描述:this

  1. debounce函數會經過閉包維護一個timer
  2. 當同一action在delay的時間間隔內再次觸發,則清理timer,而後從新設置timer

能夠在Chrome中運行下面的代碼,看看debounce的效果,代碼Github連接code

var debounce = function(action, delay) {
    var timer = null;
    
    return function() {
        var self = this, 
              args = arguments;
              
        clearTimeout(timer);
        timer = setTimeout(function() {
            action.apply(self, args)
        }, delay);
    }
}

// example
function resizeHandler() {
    console.log("resize");
}

window.onresize = debounce(resizeHandler, 300);

throttle實現

throttle跟debounce的最大不一樣就是,throttle會有一個閥值,當到達閥值的時候action一定會執行一次。

因此throttle的實現能夠基於前面的debounce的實現,只須要加上一個閥值,代碼Github連接

var throttleV1 = function(action, delay, mustRunDelay) {
    var timer = null,
          startTime;
          
    return function() {
        var self = this, 
              args = arguments, 
              currTime = new Date();
              
        clearTimeout(timer);
        
        if(!startTime) {
            startTime = currTime;
        }
        
        if(currTime - startTime >= mustRunDelay) {
            action.apply(self, args);
            startTime = currTime;
        }
        else {
            timer = setTimeout(function() {
                action.apply(self, args);
            }, delay);
        }
    };
};

其實,對於上面的實現能夠進心簡化,只是經過閉包維護一個開始的時間:

var throttleV2 = function(action, delay){
    var statTime = 0;
    
    return function() {
        var currTime = +new Date();
        
        if (currTime - statTime > delay) {
            action.apply(this, arguments);
            statTime = currTime ;
        }
    }
}    

// example
function resizeHandler() {
    console.log("resize");
}

window.onresize = throttleV2(resizeHandler, 300);

總結

經過前面的介紹,應該對debounce和throttle有一個直觀的認識了:

  • debounce:把觸發很是頻繁的事件合併成一次執行
  • throttle:設置一個閥值,在閥值內,把觸發的事件合併成一次執行;當到達閥值,一定執行一次事件

瞭解了throttle和debounce以後,下面看看他們的經常使用場景:

debounce

  • 對於鍵盤事件,當用戶輸入比較頻繁的時候,能夠經過debounce合併鍵盤事件處理
  • 對於ajax請求的狀況,例如當頁面下拉超過必定返回就經過ajax請求新的頁面內容,這時候能夠經過debounce合併ajax請求事件

throttle

  • 對於鍵盤事件,當用戶輸入很是頻繁,可是咱們又必需要在必定時間內(閥值)內執行處理函數的時候,就可使用throttle

    • 例如,一些網頁遊戲的鍵盤事件
  • 對於鼠標移動和窗口滾動,鼠標的移動和窗口的滾動會帶來大量的事件,可是在一段時間內又必須看到頁面的效果

    • 例如對於能夠拖動的div,若是使用debounce,那麼div會在拖動中止後一會兒跳到目標位置;這時就須要使用throttle
相關文章
相關標籤/搜索