JS throttle與debounce的區別

JS throttle與debounce的區別

一般在項目中我們會對input、scroll、resize等事件進行節流控制,防止事件過多觸發,減少資源消耗;在vue的官網的例子中就有關於lodash的debounce方法的使用,當時也提到了throttle,但一直沒搞明白節流 throttle去抖 debounce具體區別在哪裏,所以花了點時間來搞清楚。

1. 區別

節流 throttle去抖 debounce的區別主要在觸發時機上:

  • debounce(func, wait, options):創建並返回函數的防反跳版本,將延遲函數的執行(真正的執行)在函數最後一次調用時刻的wait毫秒之後,對於必須在一些輸入(多是一些用戶操作)停止之後再執行的行爲有幫助。將一個連續的調用歸爲一個,如果連續在wait毫秒內調用,最後只有最後一次會執行
  • throttle(func, wait, options):創建並返回一個像節流閥一樣的函數,當重複調用函數的時候,最多每隔指定的wait毫秒調用一次該函數;不允許方法在每wait毫秒間執行超過一次,如果連續在wait毫秒內調用,最後執行會均勻分佈在大約每wait一次

對於lodash來說,throttle是調用debounce來實現的,throttle 和 debounce 最終都會都會調用 debounce 方法。當調用 _.debouncelodash會返回一個函數,這個函數在被調用時會生成一個 setTimeout(delayed, delay)。其中 delayed 又是一個內部方法,在 delayed 被調用時進行如下檢測:當前時間 - 上次func被調用事件 是否 小於 0 或 大於 delay ?如果是則執行一次 func,記錄並返回執行結果,同時更新上次被調用時間;如果不是則調用 setTimeout 進行下一次的判斷。_.throttle 方法只不過是多給 debounce 傳了一個 options = {maxWait: $maxWait, leading: true, trailing: true},這個選項的意思是至少保證在每 maxWait 時間讓 func 被調用一次。

可以看下面的栗子

這個圖中圖中每個小格大約30ms,右邊有原生mouseover事件、lodash與jQuery節流去抖插件的debounce與throttle事件。 在圖左區域移動鼠標時:對於debounce,mouseover事件一直沒有被調用,直到停下來才被調用一次。而throttle是每wait毫秒就調用一次。

2. 使用場景

debounce:第一次觸發後,進行倒計wait毫秒,如果倒計時過程中有其他觸發,則重置倒計時;否則執行。用它來丟棄一些重複的密集操作,直到流量減慢。 throttle:第一次觸發後先執行fn(lodash可以通過{leading: false}來取消),然後wait ms後再次執行,在單位wait毫秒內的所有重複觸發都被拋棄。即如果有連續不斷的觸發,每wait ms執行fn一次,用在每隔一定間隔執行回調的場景。

  • mouse move 時減少計算次數:debounce
  • input 中輸入文字自動發送 ajax 請求進行自動補全: debounce
  • ajax 請求合併,不希望短時間內大量的請求被重複發送:debounce
  • resize window 重新計算樣式或佈局:debouncethrottle
  • scroll 時觸發操作,如隨動效果:throttle
  • 對用戶輸入的驗證,不想停止輸入再進行驗證,而是每n秒進行驗證:throttle

3. 簡單實現

3.1 去抖 debounce

按照上面的說明,去抖就是連續多次delay內的操作取最後一次操作真正執行。

let reduceEvent
function debounce(cb, delay) {
  if (!reduceEvent) {
    reduceEvent = setTimeout(() => {
      cb()
      console.log('執行啦!!')
      reduceEvent = null
    }, delay)
  }
}

setTimeout(() => debounce(() => console.log(1), 2000), 1000)         // 打印: 1 執行啦!!
setTimeout(() => debounce(() => console.log(2), 2000), 2000)
setTimeout(() => debounce(() => console.log(3), 2000), 2000)
setTimeout(() => debounce(() => console.log(4), 2000), 4000)         // 打印: 4 執行啦!!

3.2 節流 throttle

按照上面的說明,節流就是連續多次delay內的操作按照指定的間隔來執行。

function throttle(func, wait = 200) {
  let last = 1
  let timer
  return function(...rest) {
    const now = +new Date()
    if (last && now - last < wait) {
      clearTimeout(timer)
      timer = setTimeout(() => {
        last = now
        func.apply(this, rest)
      }, wait)
    } else {
      last = now
      func.apply(this, rest)
      clearTimeout(timer)
    }
  }
}
const task = throttle(() => console.log(1), 2000)
setTimeout(task, 0)
setTimeout(task, 500)
setTimeout(task, 1000)
setTimeout(task, 2000)                 // 打印: 1 1

網上的帖子大多深淺不一,甚至有些前後矛盾,在下的文章都是學習過程中的總結,如果發現錯誤,歡迎留言指出~

參考:

  1. lodash
  2. 圖解 debounce 與 throttle 的區別
  3. debounce與throttle區別
  4. Debouncing and Throttling Explained Through Examples
  5. Debounce and Throttle: a visual explanation
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章