淺談 JavaScript 中的防抖與節流(一)

落秋發表於2019-12-19

簡介

防抖與節流都是用來限制使用者頻發觸發事件的機制,下面我將簡單介紹一下,防抖與節流的區別及其應用場景

防抖

當使用者觸發某次事件後,若在規定時間內不再觸發該事件,則這個事件才會被真正響應。我們稱這樣的機制為防抖

舉例說明

現規定一輛大巴,從最後一位上車人的時間算起,在等待十分鐘後,才允許發車。

若我們設定最後一個上車人的時間設為Tn,那麼發車的時間則為Tn + 10min。

在上述例子中,我們不難發現,大巴發車的時間是不固定的,是隨著最後一位上車人的時間而動態改變的。如果大巴在等待發車的過程中,不斷有人上車,那麼大巴真正的發車時間則不斷向後推移,並且始終與最後一位上車人的時間保持著十分鐘的時間差。

觸發與響應

我們可以將人上車這一動作比作事件的觸發,而大巴發車則比作事件的響應。我們可以知道,在防抖機制裡面,事件的響應時間(也就是事件真正開始執行的時間)是隨這個事件最後被觸發的時間而發生變化的。

我們再做一個假設,現有一個按鈕,當使用者點選這個按鈕時,並且要求在十秒內不再點選這個按鈕,才會向後臺傳送請求。

我們設定觀察時間為三十秒,T代表使用者點選按鈕,R代表向後臺傳送請求

一、若使用者每隔五秒點選一次按鈕,則事件時間示例圖下:

防抖與節流

由上圖,我們可以看到,因為使用者每次點選按鈕的時間間隔不足十秒,所以在這觀察的三十秒內並不會有向後臺傳送的請求。

二、若使用者每隔十五秒點選一次按鈕,則事件時間示例圖下:

防抖與節流

而這一次,當我們將使用者每次點選按鈕的時間間隔保持在十秒以上時,則一共向後臺傳送了兩次請求:R1和R2。

程式碼示例

只要簡單的幾行程式碼便可實現防抖機制

/**
 * @desc 函式防抖
 * @param func 函式
 * @param wait 延遲執行毫秒數
 */
function debounce(func, wait) {
    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}

Tips:apply() 方法是用來改變this指向的

在JavaScript中,防抖主要是通過setTimeout來實現的。當事件被觸發時,則建立一個計時器,只有當約定的時間到了才會真正執行事件,但是當事件還未執行,而這個事件又再次被觸發了,則將上一計時器清除,重新建立一個新計時器,也就是重新計算時間的意思。

在Vue中的使用

以Vue為例,實現我們開頭舉的大巴發車的例子

<template>
  <div id="app">
    <button @click="addPeople">有人上車</button>
    <h1>發車次數:{{ runNum }}</h1>
    <h1>車上人數:{{ people }}</h1>
    <h1>距離最後一個人上車已等待:{{ time }}秒</h1>
  </div>
</template>
<script>
/**
 * @desc 函式防抖
 * @param func 函式
 * @param wait 延遲執行毫秒數
 */
function debounce(func, wait) {
  let timeout
  return function () {
    let context = this
    let args = arguments
    if (timeout) clearTimeout(timeout)
    timeout = setTimeout(function() {
      func.apply(context, args)
    }, wait)
  }
}

export default {
  data() {
    return {
      runNum: 0, // 發車次數
      people: 0, // 上車人數
      time: 0,
      interval: ''
    }
  },
  watch: {
    people: debounce(function (val) {
      this.runNum++
      clearInterval(this.interval)
    }, 10000)
  },
  methods: {
    addPeople () {
      this.clearTime()
      this.people++
      this.startTime()
    },
    startTime () {
      this.interval = setInterval(() => {
        this.time++
      }, 1000)
    },
    clearTime () {
      this.time = 0
      clearInterval(this.interval)
    }
  }
}
</script>

淺談 JavaScript 中的防抖與節流(一)

參考來源

JavaScript專題之跟著underscore學防抖

我欲乘風歸去

相關文章