JavaScript中实现防抖 / 节流

95 阅读4分钟

1.防抖:

1.防抖函数的概念

函数防抖是一种用于限制函数执行频率的技术。在JavaScript中,函数防抖常用于处理频繁触发的事件,如浏览器窗口的resize事件、滚动事件、输入框的输入事件等。它通过延迟函数的执行来确保在一定时间内只执行一次。

2.防抖函数原理

1.当事件被触发时,不立即执行事件处理函数,而是等待一段时间(延迟时间)。

2.如果在这段时间内事件再次被触发,则重新计时,延迟时间重新开始计算。

3.直到延迟时间内没有再次触发事件,才执行事件处理函数。

3.防抖函数的好处

可以避免频繁触发事件造成不必要的资源消耗或执行结果混乱的情况,而只关注最后一次触发事件的结果,比如输入搜索框,如果正常输入,会频繁地触发并且频繁地请求后端接口,这样就造成了不必要的资源消耗,但是当我们使用防抖,就可以实现用户输入完之后再统一触发请求接口

4.防抖函数的应用

输入框输入联想搜索:用户在输入框中连续输入字符时,只在用户输入完成后一段时间内进行搜索,而不是在每次输入时都进行搜索,从而减少不必要的网络请求和搜索结果的更新频率。

按钮点击事件:在用户点击按钮时执行一些操作,但如果用户连续点击同一个按钮多次,只在最后一次点击后执行操作,避免多次点击造成的重复操作或界面跳转。

浏览器窗口resize事件:当用户调整浏览器窗口大小时触发resize事件,使用函数防抖可以确保只在用户停止调整窗口大小后执行相应的操作,例如重新布局页面或重新计算元素尺寸。

5.最终实现代码

<input type="text" />
<script>
  // 防抖函数的实现
  const debounce = (fn, delay, immediate = false, resultCallback) => {
    // 用于记录上一次事件触发的timer
    let timer = null;
    let isInvoke = false;
    // 触发事件是执行的函数
    const _debounce = (...args) => {
      return new Promise((resolve, reject) => {
        try {
          // 如果有再次触发或者更多次触发,则取消上一次事件
          if (timer) clearInterval(timer);
          // 首次操作不需要延时
          let res = undefined;
          if (immediate && !isInvoke) {
            res = fn.apply(this, args);
            if (resultCallback) resultCallback(res);
            resolve(res);
            isInvoke = true;
            return;
          }
          // 延迟执行对应的fn函数
          timer = setTimeout(() => {
            res = fn.apply(this, args);
            if (resultCallback) resultCallback(res);
            resolve(res);
            timer = null;
            isInvoke = false;
          }, delay);
        } catch (err) {
          reject(err);
        }
      });
    };
    // 给_debounce绑定一个取消的函数
    _debounce.cancel = () => {
      if (timer) clearTimeout(timer);
      timer = null;
      isInvokeable = false;
    };
    // 返回一个新的函数
    return _debounce;
  };
  
  // 测试
  const inputEl = document.querySelector('input');
  const testInputValue = debounce((e) => {
    console.log(e.target.value)
  },200)
  inputEl.oninput = testInputValue
</script>

2.节流

1节流函数的概念

节流函数是一种用于控制函数执行频率的技术。它可以确保函数在一定时间间隔内最多只执行一次,从而避免频繁触发导致性能问题或不必要的资源消耗。

2.节流函数原理

节流函数通过延迟执行处理函数的方式来解决这个问题。它会设置一个时间间隔,只有当两次事件触发的时间间隔超过了这个设定的时间间隔时,才会执行处理函数。如果两次触发事件的时间间隔小于设定的时间间隔,那么处理函数就不会被执行,从而实现了控制函数执行频率的效果。

3.节流函数的实现方式

定时器版节流:使用 setTimeout 来延迟执行处理函数,每次事件触发时设置一个定时器,在定时器到期后执行处理函数,并清除定时器。 时间戳版节流:记录上次执行处理函数的时间戳,在每次事件触发时先判断距离上次执行的时间是否超过设定的时间间隔,如果超过则执行处理函数,否则忽略。

4.节流函数的应用

节流函数在实际开发中经常被用来优化高频触发的事件处理,例如在页面滚动、输入框输入,阻止按钮频繁触发等场景下,通过节流来控制函数的执行频率,从而提升页面性能和用户体验。

5.最终实现代码

<script>
  // 节流函数的实现
  const throttle = (
    fn,
    interval,
    { leading = true, trailing = false } = {}
  ) => {
    let startTime = 0;
    let timer = "";
    const _throttle = (...args) => {
      return new Promise((resolve, reject) => {
        try {
          // 获取当前时间
          const nowTime = new Date().getTime();
          // 对立即执行进行控制
          if (!leading && startTime === 0) {
            startTime = nowTime;
          }
          // 计算需要等待的时间执行函数
          const waitTime = interval - (nowTime - startTime);
          if (waitTime <= 0) {
            if (timer) clearInterval(timer);
            const res = fn.apply(this, args);
            resolve(res);
            startTime = nowTime;
            timer = null;
            return;
          }
          // 判断是否需要执行尾部
          if (trailing && !timer) {
            timer = setTimeout(() => {
              const res = fn.apply(this, args);
              resolve(res);
              startTime = new Date().getDate();
              timer = null;
            }, waitTime);
          }
        } catch (err) {
          reject(err);
        }
      });
    };
    return _throttle;
  };
  
  // 测试
  const test = throttle(
    (data) => {
      return data;
    },
    1000,
    { trailing: true }
  );
  const click = () => {
    test("hello").then((res) => {
      console.log(res);
    });
  };
  click();
  click();
  click();
</script>