防抖和節流的應用場景和實現

後排的風過發表於2018-10-30

防抖

防抖和節流的應用場景和實現
防抖就是將一段時間內連續的多次觸發轉化為一次觸發。

使用場景

一般可以使用在使用者輸入停止一段時間過後再去獲取資料,而不是每次輸入都去獲取,如下圖:

防抖和節流的應用場景和實現

實現程式碼

  function debounce<Return>(
    fn: (...params: any[]) => Return, 
    wait: number, /** 等待時間 */
    immediate: boolean /** 是否立刻執行一次 */
  ): Executor<Return> {
    const now: () => number = Date.now.bind(Date);
    let lastTime: number = 0;
    let timer: number = null;
    let params: IArguments = null;
    let _this: Function | null = null;

    function later(): void {
      const nowTime: number = now();

      if (nowTime - lastTime < wait) {
        // 如果還不夠等待時間則繼續等待
        const remainTime = wait - (nowTime - lastTime);

        timer = setTimeout(later, remainTime);
      } else {
        // 已到等待時間,執行回撥
        debouncer.result = fn.apply(_this, params);

        timer = null;
        _this = null;
        params = null;
      }
    }

    function execute(): (Return | null) {
      lastTime = now();
      _this = this;
      params = arguments;

      try {
        if (immediate && timer === null) {
          // 立刻執行一次
          debouncer.result = fn.apply(_this, params);
        }

        return debouncer.result;
      } finally {
        if (timer === null) {
          // 加入時間佇列,等待執行
          timer = setTimeout(later, wait);
        }
      }
    }

    // 建立執行器
    const debouncer: Executor<Return> = {
      execute,
      result: null,
    };

    return debouncer;
  };
複製程式碼

原理很簡單,主要是判斷是否到達等待時間,如果沒到達的話就繼續加入任務佇列等待執行。使用方法:

import utils from '../index';

const input = document.querySelector('input');
const executor = utils.fn.debounce(function(value) {
  console.log('fetch');
  
  return value;
}, 300);

let value = null;

input.addEventListener('input', function(e) {
  executor.execute(e.target.value);
  value = executor.result;
});
複製程式碼

返回一個執行器的原因是這樣可以方便獲取最後一次函式執行時返回的值。

節流

防抖和節流的應用場景和實現
節流顧名思義則是將減少一段時間內觸發的頻率。

使用場景

可以將一些事件降低觸發頻率。比如懶載入時要監聽計算滾動條的位置,但不必每次滑動都觸發,可以降低計算的頻率,而不必去浪費資源;另外還有做商品預覽圖的放大鏡效果時,不必每次滑鼠移動都計算位置。

實現程式碼

throttle: function <Return>(
    fn: (...params: any[]) => Return,
    wait: number,
    {
      isExecuteAtStart = true,
      isExecuteAtEnd = true,
    }: ThrottleOptions = {
      isExecuteAtStart: true,
      isExecuteAtEnd: true,
    }
  ): Executor<Return> {
    let timer: number = null;
    let _this: Function = null;
    let params: IArguments = null;

    function execute(): (Return | null) {
      _this = this;
      params = arguments;

      if (isExecuteAtStart && timer === null) {
        // 如果需要開始的時候執行且沒有計時器
        executor.result = fn.apply(_this, params);
        _this = null;
        params = null;
      }

      if (isExecuteAtEnd) {
        // 如果結束的時候需要執行
        if (timer === null) {
          timer = setTimeout(function () {
            executor.result = fn.apply(_this, params);
            _this = null;
            params = null;
            timer = null;
          }, wait);
        }
      }

      return executor.result;
    }

    const executor: Executor<Return> = {
      execute,
      result: null
    };

    return executor;
}
複製程式碼

最後

防抖和節流的目的都是為了減少不必要的計算,不浪費資源,只在適合的時候再進行觸發計算。原始碼可以在這個專案裡的fn模組看到,另外還有許多實用的功能,歡迎使用和學習。如果我的文章對你有些幫助的話,可以點個贊,謝謝~

相關文章