什么是防抖 (debounce)? 如何实践防抖 (debounce) 函式?

2022年12月11日

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

防抖(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 上追蹤我們