throttle & debounce(節流&防抖)
轉載自https://www.cnblogs.com/wilber2013/p/5893426.html
白話debounce和throttle
遇到的問題
在開發過程中會遇到頻率很高的事件或者連續的事件,如果不進行效能的優化,就可能會出現頁面卡頓的現象,比如:
- 滑鼠事件:mousemove(拖曳)/mouseover(劃過)/mouseWheel(滾屏)
- 鍵盤事件:keypress(基於ajax的使用者名稱唯一性校驗)/keyup(文字輸入檢驗、自動完成)/keydown(遊戲中的射擊)
- window的resize/scroll事件(DOM元素動態定位)
為了解決這類問題,常常使用的方法就是throttle(節流)
下面就具體的看看兩者的相似和區別。
認識throttle和debounce
throttle和debounce的作用就是確認事件執行的方式和時機,以前總是不太清楚兩者的區別,容易把二者弄混。
下面就通過兩個簡單的場景描述一下debounce和throttle,以後想到這兩個場景就不會再弄混了:
debounce
假設你正在乘電梯上樓,當電梯門關閉之前發現有人也要乘電梯,禮貌起見,你會按下開門開關,然後等他進電梯;
如果在電梯門關閉之前,又有人來了,你會繼續開門;
這樣一直進行下去,你可能需要等待幾分鐘,最終沒人進電梯了,才會關閉電梯門,然後上樓。
所以debounce的作用是,當呼叫動作觸發一段時間後,才會執行該動作,若在這段時間間隔內又呼叫此動作則將重新計算時間間隔。
throttle
假設你正在乘電梯上樓,當電梯門關閉之前發現有人也要乘電梯,禮貌起見,你會按下開門開關,然後等他進電梯;
但是,你是個沒耐心的人,你最多隻會等待電梯停留一分鐘;
在這一分鐘內,你會開門讓別人進來,但是過了一分鐘之後,你就會關門,讓電梯上樓。
所以throttle的作用是,預先設定一個執行週期,當呼叫動作的時刻大於等於執行週期則執行該動作,然後進入下一個新的時間週期
簡單實現
有了上面的瞭解,就可以去實現簡單debounce和throttle了。
debounce實現
首先來看看debounce的實現,根據前面對debounce的描述:
- debounce函式會通過閉包維護一個timer
- 當同一action在delay的時間間隔內再次觸發,則清理timer,然後重新設定timer
可以在Chrome中執行下面的程式碼,看看debounce的效果,程式碼Github連結:
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