Writing

防抖與節流

文章發表於

前言

想像一下,當你在某個網站透過搜尋框尋找東西時,你輸入的每一個字都會立即觸發一次自動推薦(auto suggest)的 API,這樣的使用體驗可能會讓使用者直接離開,轉向競品。

雖然功能看似簡單,但如果沒有妥善處理,可能會造成一連串的問題:

  • 伺服器過載:過於頻繁的 API 請求會對伺服器造成巨大的負擔。如果後端沒有做流量控管,甚至可能導致伺服器崩潰。
  • 競態條件(Race Condition):例如,你先輸入「apple is」,接著立刻改成「banana」,由於請求回應的時間差,畫面上可能同時有機會出現「apple is」或「banana」的推薦結果。
  • 介面卡頓:當每輸入一個字就立即更新結果,畫面會不停地在「載入中」與「顯示結果」之間閃爍切換,造成突兀且不舒服的使用體驗。

這篇文章將會介紹兩種常見的解決方案,來解決類似的問題:防抖(Debounce)和節流(Throttle)。

節流

什麼是節流?

節流是一種讓函式執行「平均分布在時間軸上」的技巧,無論觸發事件的頻率有多高,節流確保函式在指定的間隔內最多被呼叫一次,例如設定每秒執行一次,那即使事件連續被觸發 100 次,也只會每秒處理一次,節奏是固定的。

核心原理

  • 第一次函式呼叫時立即執行
  • 設定一個冷卻期(cooling period)
  • 在冷卻期內,忽略或記錄新的函式呼叫
  • (選用) 冷卻期結束後,可以再次執行上一個函式

使用場景

節流適合用在「需要穩定執行頻率」的情境,例如:

  • 捲動事件:限制捲動事件處理頻率
  • 視窗調整大小:限制調整大小時的重新計算
  • 滑鼠移動:限制滑鼠移動事件處理頻率
  • 遊戲控制:限制按鍵事件處理頻率
  • 拖曳和繪圖操作:確保平滑的視覺效果

Throttle 的實作

這個基本實作在大多數簡單場景中已經足夠,比如限制按鈕點擊或基本的滾動事件處理。

function throttle(func, wait) {
let shouldThrottle = false;
return function (...args) {
if (shouldThrottle) return;
shouldThrottle = true;
func.apply(this, args);
setTimeout(() => {
shouldThrottle = false;
}, wait);
};
}

防抖

什麼是防抖?

防抖是另一種控制函式執行頻率的技術,它保證函式在指定時間「最後一次」觸發後才執行一次。簡單來說,只有在一段時間內沒有新的觸發時,才真正執行函式;節流則是讓函式以固定頻率執行,即使中間有很多觸發,也只會定期處理一次。

核心原理

  • 第一次觸發時,設定一個冷卻期(cooling period)
  • 在冷卻期內,若有新的觸發,就取消上一次的計時器並重置冷卻期
  • 冷卻期結束後,執行函式,並且清除計時器

使用場景

防抖特別適合用在「等待使用者停下來再做事」的情境,換句話說就是,它最適合應對那種短時間內會被大量觸發的事件,但我們其實只在意「最後一次」的狀況。

  • 搜尋建議:使用者暫停輸入後才發起搜尋請求。
  • 表單驗證:輸入框停止輸入後才觸發驗證。
  • 自動儲存:使用者停止編輯一段時間後才自動儲存。

防抖的實作

function debounce(func, wait) {
let timeId = null;
return function (...args) {
if (timeId) {
clearTimeout(timeId);
}
timeId = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}

節流 & 防抖比較

模式(Pattern)使用時機(Use When)範例(Example)
防抖(Debounce)只關心使用者停止操作後的最終值搜尋建議、表單驗證、自動儲存
節流(Throttle)需要在操作過程中定期獲取更新捲動位置追蹤、視窗大小變化處理、進度更新
如果您喜歡這篇文章,請點擊下方按鈕分享給更多人,這將是對筆者創作的最大支持和鼓勵。
Buy me a coffee