什麼是防抖 (debounce)?如何實踐防抖 (debounce) 函式?

2024年3月8日

💎 加入 E+ 成長計畫 與超過 300+ 位軟體工程師一同在社群中成長,並且獲得更多的軟體工程學習資源

防抖 (debounce)節流 (throttle) 絕對是在考手寫題時最常出現的前幾名,這兩者都能做到優化,但使用情境不太相同,本篇介紹防抖函式,在這篇文章中會繼續討論節流。

防抖 (debounce) 在做什麼?

防抖 (debounce) 函式是指,將多次操作優化為,只在最後一次執行。具體來說,當一定時間內沒有持續觸發事件時,事件處理函式才會被執行一次,但如果在設定的時間內又一次觸發了事件,就會重新開始計時。

在手寫防抖 (debounce) 函式前,我們先來了解防抖的使用情境。

我們以 Google 搜尋框和搜尋建議列表為例子。畫面如下,有一個搜尋框,和一個搜尋建議列表,使用者在此搜尋框輸入文字後,搜尋建議列表會即時呈現結果; 搜尋框的文字只要一改變,搜尋建議列表也會即時更新結果。

Google Search 畫面
Google Search 畫面
圖片來源:google.com

這樣看起來是很理想的設計吧? 但首先要去解決一個問題。如果使用者一直在搜尋框內書寫文字,這樣會一直觸發 API 去更新搜尋建議列表,例如使用者想搜尋 javascript,這時 API 也及時觸發,所以此 API 會被觸發 10 次,而且前 9 次不會是使用這想要的結果。

為了提升使用者體驗以及優化程式嗎,這一段功能我們就可以透過防抖(debounce) 來優化。

防抖函式會接受兩個參數

  • 延遲的時間 (ms)
  • 要執行的函式

以上面搜尋框的例子來說,我們透過防抖就可以完成:當使用者停止在搜尋框內打入文字超過一定的時間,此時才會去執行觸發 API 的函式。

手寫防抖 (debounce) 函式

我們先直接看程式碼,看看你能了解多少。有不懂的地方也不擔心,下面會透過註解,一行行解釋:

function debounce(fn, delay = 500) {
  let timer;

  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn(...args);
    }, delay);
  };
}

程式碼註解版本和實際應用

// debounce function 接受兩個參數
// 一是:要執行的 function
// 二是:要延遲的豪秒數,這邊預設 500 毫秒
function debounce(fn, delay = 500) {
  let timer;

  // debounce function 最終會回傳一個 function
  return (...args) => {
    // 每一次 debounce function 被觸發時,會先清除之前的 timer,避免觸發先前的 fn 函式
    // 因此只要在 delay 時間內觸發 debounce function,就會一直清除先前的 timer,避免 fn 一直被執行
    clearTimeout(timer);
    // 清除之後,再重新計時
    // 當 delay 時間到時,執行 fn
    timer = setTimeout(() => {
      fn(...args);
    }, delay);
  };
}

// updateDebounceText 會在延遲 500 ms 後執行 console.log('call api get search result')
const updateDebounceText = debounce((text) => {
  console.log("call api get search result");
}, 500);

// 搜尋框監聽 input 事件,當 input 改變時
// 觸發 updateDebounceText 函式
searchInput.addEventListener("input", (e) => {
  updateDebounceText(e.target.value);
});
🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們