用生活中的例子理解防抖與節流 Debounce & Throttle in daily life
關於 Debounce & Throttle 正經詳細的教學已經不少
如果還是無法完全體會,那麼不妨換一個角度來理解看看
這篇文章會用搭公車與接電話來比喻兩者的運作邏輯
也許能加深印象!
Debounce & Throttle 是什麼功能
網頁中常常會有 監聽A事件 => 產生B動作 的情境
- 監聽瀏覽器捲軸的滾動事件,並顯示動畫
- 監聽input內容,並產生自動補全
如果沒有做 Debounce 或 Throttle
就會因為監聽到時時刻刻的變化,並產生動作而消耗大量效能
例如: 只是滾動捲軸就可以產生數十到數百個事件
《Debouncing and Throttling Explained Through Examples》by David Corbacho
https://css-tricks.com/debouncing-throttling-explained-examples/
而實際應用中,我們不需要緊迫盯人,時時刻刻都在監看變化
不但浪費效能,甚至會給使用者”喂! 我還沒完成 幹嘛亂動”的感覺
例如:
- 輸入框的自動自動補全,可以等到使用者打字停止後200毫秒後再顯示
- 每過200毫秒,再檢查一次捲軸滾動的位置是否要顯示動畫了
所以 Debounce 與 Throttle 都是節省效能的作法
Debounce 防抖實作與生活舉例
Debounce 的實作,在生活中就好像在搭公車一樣
公車停在站牌後,會等大家排隊上車
等到大家上車後,才一次出發
- 排隊上車的人 => 監聽的事件,如: 捲軸滾動、輸入框打字
- 公車出發 => 對應的事件,如: 顯示動畫、顯示自動補全
而程式碼的邏輯則是
接收到執行的指令時,會延遲一段時間才執行
如果這段時間內又被觸發,就重新倒數
// debounce 實作
const debounce = (fn, delay) => {
let timeoutID = null;
return (...args) => {
clearTimeout(timeoutID);
timeoutID = setTimeout(() => {
fn(...args);
}, delay);
};
};
完整範例程式碼,可以動手玩玩:
Throttle 節流實作與生活舉例
Debounce 的實作,在生活中就好像客服在接電話一樣
有人打電話進來後,就會進入忙線狀態(假設每通電話時間固定)
忙線時,再有其他人打進來也不會理會
直到時間結束才會接下一通處理
- 打進來的人 => 監聽的事件,如: 捲軸滾動、輸入框打字
- 接通處理 => 對應的事件,如: 顯示動畫、顯示自動補全
而程式碼的邏輯
除了接電話,撰文時也覺得其實很像遊戲放技能
接收到執行的指令時,會檢查是不是在忙線中、CD是不是還在冷卻
如果這段時間內又監聽到事件,就不會觸發後續動作,節省效能
直到冷卻完成才能再接下一通、才能再放技能
// throttle 實作
const throttle = (fn, delay) => {
let timeoutID = null;
return (...args) => {
if (timeoutID) return;
timeoutID = setTimeout(() => {
fn(...args);
timeoutID = null;
}, delay);
};
};
完整範例程式碼,可以動手玩玩:
註記與結語
本篇的範例是比較單純的 Debounce & Throttle
也是為了方便初學的人學習
在 loadash, underscore 等函式庫中
提供了其他參數,可以應用更廣泛的情境
例如: leading 參數可以讓 Debounce 變成先執行動作後,才進入延遲CD
所以延遲與執行的先後,不能當作區別兩者的方式
兩者最大的區別
- Debounce 是把一連串的動作,變成只執行一次
- Throttle 即使一連串動作超長,每隔一段緩衝時間,還是會執行一次
了解了這兩種方式後,除了輸入框與捲動軸以外
例如:拖曳物件、需要消耗大量效能的畫面渲染(圖表、地圖的互動)、避免重複發送大量 API request
都可以考慮加入 Debounce & Throttle 節省效能的設計
幫助你的網頁體驗與精緻度大幅提升!