[譯]例項詳解防抖與節流(乾貨!!!)

陳小俊發表於2018-11-07

lodash原始碼中推薦的文章,為了學習(英語),翻譯了一下~

原文連結

作者:DAVID CORBACHO

本文來自一位倫敦前端工程師DAVID CORBACHO的技術投稿。我們在之前討論過這個話題(關於防抖與節流),但這次,DAVID CORBACHO通過生動的演示會將它們講的十分清晰,通俗易懂。

Debouncethrottle是兩個相似(但實現原理不一樣)的技術手段,用於控制一個函式在一段時間內執行幾次。

當我們的函式附著在dom事件上時,使用Debouncethrottle去處理這個函式是十分有用的。為什麼呢?因為我們在事件和執行函式之間加了一個控制層。需要注意的是,這裡並不是去控制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的例子。

Example of a

underscore.js中,該配置項叫immediate而不是leading

你可以試試:

例子連結

Debounce 的實現

我第一次看到debounce的實現是在John Hann(term之父)部落格中,當時還是2009年。一年過後Jeremy Ashkenas將它加入了underscore.jsdebounce最近才加入到Lodash中。

這三種實現方式內部有些不同,但他們的介面十分相似。

曾經有一段時間underscore採用了debounce中debounce/throttle 的實現,知道2013年我在_.debounce中發現了一個bug,從那之後,他們分道揚鑣。

Lodash加了很多特徵在_.debounce_.throttle中。原來的immediate標識被替換成leadingtrailing。你可以配置一項,或者都配置。預設生效的是trailing

我在本文中不會討論新的配置項maxWait,雖然我不討論他,但是他很有用。事實上throttle的實現就是在debounce中使用了maxWait,你可以在這裡看到。

Debounce 舉例

Resize 的例子

當我們在調整瀏覽器視窗時,會觸發Resize事件。

看下面的demo

demo

可以看到,我們在resize事件中使用預設配置trailing,因為我們在調整視窗大小後只去最後一次的值。

鍵盤輸入自動傳送ajax請求

我們做的處理是當使用者在輸入時,每50毫秒向後臺傳送一次ajax請求。這時使用_.debounce能幫我們避免許多額外的消耗,我們僅僅在使用者停止輸入後傳送一次請求。

這裡使用leading是沒有意義的,我們需要等待使用者最後一個字元敲下。

例子連結

類似此場景的一個例子是進行輸入驗證,比如使用者在註冊時提示“密碼不足6位”。

如何使用debounce和throttle以及常見的坑

許多人往往更傾向於寫自己的debounce/throttle函式,或者ctrlC ctrlV別人部落格裡的程式碼。我的建議是正確的去使用underscoreLodash。如果你僅僅需要_.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的作用是確保我們的函式在每一個毫秒區間只執行一次。

Throttledebounce主要的不同在於,監聽的事件一直在發生,Throttle能確保我們執行的函式在一個毫秒區間內至少執行一次。這裡可能一時難以理解,但看了下面的例子之後,也許你就會茅塞頓開。

Throttling Examples

無限下拉

一個常見的場景,使用者在下拉頁面,你需要去監測使用者離底部多遠,如果接近底部時,傳送一個ajax請求獲取更多內容,然後再拼接到頁面上。

討巧的debounce在這個場景下一點辦法都沒有,它只會在使用者停止滑動動作後觸發。我們需要的是使用者在接近底部時傳送請求,此時使用者可能正在下拉滑動條。

有了throttle我們可以經常計算使用者離底部的距離。

例子連結

輸出有心,如有幫助,不吝君贊!


相關文章