Debounce & Throttle

防抖 Debounce

說明

  • 事件被觸發 n 秒後,再執行動作,如果在 n 秒內再次觸發的話,就重新計時
  • 單位時間內,頻繁觸發事件,只執行最後一次
  • 使用 closure 保持定時器的狀態

常用情境

  • 在搜尋 Input 框,輸入文字的關鍵字觸發,可以等到使用者輸入完畢再發送 API 請求
  • 表單驗證,確認使用者輸入完畢再檢查格式是否通過

範例程式碼

const btn = document.querySelector('.btn')
const input = document.querySelector('.input')
const result = document.querySelector('.result')

const pay = () => {
  console.log('防抖付款')
}

// 寫法1
const debounce = (cb, delay) => {
  let timer
  return function () {
    // 如果在n秒內再次觸發,清除目前的定時器,重新開始計時
    clearTimeout(timer)
    timer = setTimeout(() => {
      cb()
    }, delay)
  }
}

btn.addEventListener('click', debounce(pay, 1000))

// 寫法2 : return arrow function
const debounce2 = (cb, delay = 1500) => {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      // func.call(context);
      cb(...args)
    }, delay)
  }
}

const updateText = debounce2((text) => {
  result.textContent = text
}, 1000)

input.addEventListener('input', (e) => {
  updateText(e.target.value)
})
  • 在 callback function 帶入參數
function debounce(cb, delay) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      cb(...args)
    }, delay)
  }
}

function print(str) {
  console.log(str)
}

const debouncedPrint = debounce(print, 1000)

debouncedPrint('Hello, World!')

節流 Throttle

說明

  • 在 n 秒內被連續觸發,也只執行一開始的那一次
  • 規定一定的時間內,只能執行一次動作
  • 使用 closure 保持定時器的狀態

常用情境

  • 高頻率事件在每次觸發時,都會調用綁定在事件上的 callback function,造成資源浪費
  • 例如滑鼠移動(mousemove)、頁面尺寸縮放(resize)、滾動條滾動(scroll)

範例程式碼

const result = document.querySelector('.result')

//每隔1秒執行callback的內容
function throttle(cb, time = 1000) {
  let timer = null

  return (...args) => {
    //如果已經在執行中,不做任何事
    //直到timer被清除後,重新執行
    if (timer) {
      return
    }
    timer = setTimeout(() => {
      cb(...args)
      timer = null
    }, time)
  }
}

//Infinite Scroll
function scroll() {
  let clientHeight = document.documentElement.clientHeight || document.body.clientHeight
  let scrollTop = document.documentElement.scrollTop
  let scrollHeight = document.documentElement.scrollHeight

  //當滾動到頁面底部時...
  //   if (scrollTop + clientHeight + 10 >= scrollHeight) {
  //當滾動到頁面90%時...
  if ((scrollTop + clientHeight) / scrollHeight >= 0.9) {
    for (let i = 0; i <= 10; i++) {
      let p = document.createElement('p')
      p.textContent = 'testing2...'
      result.appendChild(p)
    }
  }
}

document.addEventListener('scroll', throttle(scroll))

參考資料

# JavaScript # 效能