lodash原始碼中推薦的文章,為了學習(英語),翻譯了一下~
原文連結
作者:DAVID CORBACHO
本文來自一位倫敦前端工程師DAVID CORBACHO
的技術投稿。我們在之前討論過這個話題(關於防抖與節流),但這次,DAVID CORBACHO
通過生動的演示會將它們講的十分清晰,通俗易懂。
Debounce
和throttle
是兩個相似(但實現原理不一樣)的技術手段,用於控制一個函式在一段時間內執行幾次。
當我們的函式附著在dom
事件上時,使用Debounce
和throttle
去處理這個函式是十分有用的。為什麼呢?因為我們在事件和執行函式之間加了一個控制層。需要注意的是,這裡並不是去控制dom
事件發生的頻率。
我們來看一個滑動事件的例子:
當我們使用觸控板、滾輪、或者是拉動滑動條,事件可能每秒僅僅觸發了30次左右。但如果我們滑的比較慢,他可能觸發100次。對於這些不一致的資料,你在處理的時候是否考慮到了?
2011年的時候,推特網上出現了一個問題:當你緩慢地在推特上往下滾動時,網站開始變得卡頓甚至沒有反應。John Resig
針對該問題發了一條部落格learning-from-twitter,他覺得在scroll
事件上附著複雜的函式處理是十分糟糕的。
John給出的解決方案是在scroll事件結束後,每250毫秒做迴圈執行(感興趣的可以去看上面那篇部落格,此時應該是Debounce得雛形)。這種處理耦合度低,而且避免了破壞使用者體驗。
如今處理事件的方式複雜了不少,下面向你們介紹Debounce
, Throttle
,對應的也舉一些例子。
Debounce
Debounce
將一個組的多次呼叫處理為只呼叫一次。
想象你正在電梯裡,電梯門準備關閉,這時候有個人同時進電梯,此時電梯並沒有開始上升(下降),而是電梯門再次開啟。如果不斷地有人進來,電梯將延遲他上升(或下降)的函式,從而達到資源優化的目標。
你可以自己試試,點選或者將滑鼠放在按鈕上。
可以看到debounce
將多次連續的事件整理成單次的事件。
Leading edge (or "immediate") [首次或立即]
你可能發現防抖事件在等待觸發事件執行,直到事件都結束後它才執行。為什麼不讓事件一開始就執行,從而達到跟我們最初的設想一樣的效果呢?但是短時間內不能連續執行。
你可以看看這個,這是個"leading" debounce
的例子。
在underscore.js
中,該配置項叫immediate
而不是leading
。
你可以試試:
Debounce 的實現
我第一次看到debounce
的實現是在John Hann(term之父)
的部落格中,當時還是2009年。一年過後Jeremy Ashkenas
將它加入了underscore.js。debounce
最近才加入到Lodash
中。
這三種實現方式內部有些不同,但他們的介面十分相似。
曾經有一段時間underscore採用了debounce中debounce/throttle 的實現,知道2013年我在_.debounce
中發現了一個bug,從那之後,他們分道揚鑣。
Lodash
加了很多特徵在_.debounce
和 _.throttle
中。原來的immediate
標識被替換成leading
和trailing
。你可以配置一項,或者都配置。預設生效的是trailing
。
我在本文中不會討論新的配置項maxWait
,雖然我不討論他,但是他很有用。事實上throttle
的實現就是在debounce
中使用了maxWait
,你可以在這裡看到。
Debounce 舉例
Resize 的例子
當我們在調整瀏覽器視窗時,會觸發Resize
事件。
看下面的demo
:
可以看到,我們在resize
事件中使用預設配置trailing
,因為我們在調整視窗大小後只去最後一次的值。
鍵盤輸入自動傳送ajax請求
我們做的處理是當使用者在輸入時,每50毫秒向後臺傳送一次ajax
請求。這時使用_.debounce
能幫我們避免許多額外的消耗,我們僅僅在使用者停止輸入後傳送一次請求。
這裡使用leading
是沒有意義的,我們需要等待使用者最後一個字元敲下。
類似此場景的一個例子是進行輸入驗證,比如使用者在註冊時提示“密碼不足6位”。
如何使用debounce和throttle以及常見的坑
許多人往往更傾向於寫自己的debounce/throttle
函式,或者ctrlC ctrlV
別人部落格裡的程式碼。我的建議是正確的去使用underscore
和 Lodash
。如果你僅僅需要_.debounce
和_.throttle
方法,你可以使用lodash-cli
生成指定函式的js,使用方法如下:(webpack
等打包工具的出現我覺得不必考慮此問題)
npm i -g lodash-cli
lodash include = debounce, throttle
複製程式碼
簡單使用:
// WRONG
$(window).on('scroll', function() {
_.debounce(doSomething, 300);
});
// RIGHT
$(window).on('scroll', _.debounce(doSomething, 200));
// or
var debounced_version = _.debounce(doSomething, 200);
$(window).on('scroll', debounced_version);
// If you need it
debounced_version.cancel();
複製程式碼
Throttle
Throttle
的作用是確保我們的函式在每一個毫秒區間只執行一次。
Throttle
和debounce
主要的不同在於,監聽的事件一直在發生,Throttle
能確保我們執行的函式在一個毫秒區間內至少執行一次。這裡可能一時難以理解,但看了下面的例子之後,也許你就會茅塞頓開。
Throttling Examples
無限下拉
一個常見的場景,使用者在下拉頁面,你需要去監測使用者離底部多遠,如果接近底部時,傳送一個ajax
請求獲取更多內容,然後再拼接到頁面上。
討巧的debounce在
這個場景下一點辦法都沒有,它只會在使用者停止滑動動作後觸發。我們需要的是使用者在接近底部時傳送請求,此時使用者可能正在下拉滑動條。
有了throttle
我們可以經常計算使用者離底部的距離。
輸出有心,如有幫助,不吝君贊!