Skip to content

防抖和节流

防抖函数

防抖函数的概念:

  1. 当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间
  2. 当事件频繁的被触发时,函数的触发会被频繁的推迟
  3. 只有等待了一段时间后,没有事件继续触发,函数才会执行
防抖函数的封装

实现的功能:

  1. 绑定正确的this,event对象
  2. 控制第一次立即执行,后面得执行防抖
  3. 实现取消操作
  4. 获取执行函数的返回值
js
function debounce(fn, delay, immediate = false, ) {
  let timer = null;
  //定义内部变量来控制是否第一个立即执行
  let isInvoke = false;

  const _debounce = function (...args) {
    //返回一个Promise用来处理函数的返回值
    return new Promise((resolve) => {
      if (timer) clearTimeout(timer);
      //判断是否立即执行
      if (immediate && !isInvoke) {
        let result = fn.apply(this, args);
        //promise返回 函数的返回值
        resolve(result);
        isInvoke = true;
      } else {
        timer = setTimeout(() => {
          let result = fn.apply(this, args);
          //promise返回 函数的返回值
          resolve(result);
          isInvoke = false;
          timer = null;
        }, delay);
      }
    });
  };
  //取消请求的发送
  _debounce.cancel = function () {
    if (timer) clearTimeout(timer);
    timer = null;
    isInvoke = false;
    console.log("取消成功!");
  };
  return _debounce;
}

节流函数

节流的概念:

  1. 当事件触发时,会执行这个事件的响应函数
  2. 如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数
  3. 不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的

函数的实现:在interval时间内,每次触发都创建一个定时器,创建之前先清除上一个定时器

js
function throttle(
  fn,
  interval,
  options = {
    leading: true,
    trailing: false,
  },
  resultCallback
) {
  let timer = null;
  //leading第一次是否触发,trailing最后一次是否触发
  const {leading, trailing} = options;
  let lastTime = 0;
  const _throttel = function (...args) {
    //在一段时间内最后一次触发的时间
    let nowTime = new Date().getTime();
    //如果leading=true 并且 lastTime = 0时,使第一次不触发
    if (!leading && !lastTime) lastTime = nowTime;
    //需要等待多少时间执行
    let remainTime = interval - (nowTime - lastTime);

    if (remainTime <= 0) {
      clearTimeout(timer);
      const result = fn.apply(this, args);
      if (resultCallback) resultCallback(result);
      lastTime = nowTime;
    } else {
   
      if (trailing) {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
          const result = fn.apply(this, args);
          if (resultCallback) resultCallback(result);
          lastTime = new Date().getTime();

          timer = null;
        }, remainTime);
      }
    }
  };

  _throttel.cancel = function () {
    if (timer) clearTimeout(timer);
    timer = null;
    lastTime = 0;
  };
  return _throttel;
}

函数的实现2:在interval时间内,只创建一次定时器

js
function throttle(fn, interval, options = { leading: true, trailing: false }) {
  // 1.记录上一次的开始时间
  const { leading, trailing, resultCallback } = options
  let lastTime = 0
  let timer = null

  // 2.事件触发时, 真正执行的函数
  const _throttle = function(...args) {
    return new Promise((resolve, reject) => {
      // 2.1.获取当前事件触发时的时间
      const nowTime = new Date().getTime()
     // 第一次不触发
      
      if (!lastTime && !leading) lastTime = nowTime

      // 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长时间需要去触发函数
      const remainTime = interval - (nowTime - lastTime)
      if (remainTime <= 0) {
        if (timer) {
          clearTimeout(timer)
          timer = null
        }

        // 2.3.真正触发函数
        const result = fn.apply(this, args)
        resolve(result)
        // 2.4.保留上次触发的时间
        lastTime = nowTime
        return
      }

      if (trailing && !timer) {
        timer = setTimeout(() => {
          timer = null
          lastTime = !leading ? 0: new Date().getTime()
          const result = fn.apply(this, args)
          resolve(result)
        }, remainTime)
      }
    })
  }

  _throttle.cancel = function() {
    if(timer) clearTimeout(timer)
    timer = null
    lastTime = 0
  }

  return _throttle
}

其他库

underscore: https://underscorejs.org/

loadsh:https://www.lodashjs.com/ 使用人数较多