5-函数的防抖和节流

220 阅读2分钟

函数的防抖

  • 场景1: 点击按钮向服务器发送请求(需要一定的时间),当前请求之前再点击无效点击
  • 方式1:通过一个变量,控制按钮是否可以点击
let loading = false;
SubmitEvent.onclick = async () => {
  if(loading) return;
  loading = true;
  //这里是个伪代码
  await this.$api.postSome({
    id: 11,
    remark: 777
  })
  loading = false;
}
  • 方式2: 封装一个防抖函数(可以直接用)
const utils = (function () {
  /**
   * 清除定时器
   * @param {*} timer 
   */
  const clearTimer = (timer) => {
    if (timer) {
      clearTimeout(timer);
    }
    return null;
  }
  /**
   * 函数的防抖
   * @param {Function} func 必填
   * @param {Number} wait 触发频率规则 
   * @param {Boolean} imediate 控制是否是在开始触发 
   */
  const debounce = (func, wait, imediate) => {
    if (typeof func !== "function") throw new Error('This is not a function');

    if (typeof wait === "boolean") {
      imediate = wait;
      wait = undefined;
    }
    // 处理是不是有效数字
    wait = +wait;
    if (isNaN(wait)) wait = 300;

    if (typeof imediate !== "boolean") imediate = false;

    // hander处理
    let timer = null;
    // 用箭头函数处理掉this指向额问题如果不用就func.call(this);
    return (...params) => {
      // now 记录是否是立即执行[第一次执行,并且imediate为true]
      let now = !timer && imediate;

      // 清除定时器(清除之前的)
      timer = clearTimer(timer);
      timer = setTimeout(() => {
        // 结束边界触发
        if (!imediate) func(...params);
        // 清除最后一个定时器
        timer = clearTimer(timer);
      }, wait);

      // now为true,做该干的事情
      if (now) {
        func(...params);
      }
    }
  }

  return {
    debounce
  }
})();

函数的节流

  • 一般的应用场景: input文本框的 keydown;keyup;input事件
const utils = (function () {
  /**
   * 清除定时器
   * @param {*} timer 
   */
  const clearTimer = (timer) => {
    if (timer) {
      clearTimeout(timer);
    }
    return null;
  };
  /**
   * 节流函数
   * @param {Function} func 
   * @param {Number} wait 
   */
  const throttle = (func, wait = 300) => {
    if (typeof func !== "function") throw new Error('This is not a function');
    // 处理是不是有效数字
    wait = +wait;
    if (isNaN(wait)) wait = 300;

    // handler
    let timer = null,
        previous = 0;//记录上一次触发的时间
    return (...params) => {
      let now = +new Date(),
          remaining = wait - (now - previous);
      if (remaining <= 0) {
        // 说明2次触发的间隔时间超过设定的频率,则立即执行
        func(...params);
        previous = +new Date();
        timer = clearTimer(timer);
      } else if (!timer) {
        // 间隔时间不足设定频率,而且还没设置定时器
        timer = setTimeout(() => {
          func(...params);
          previous = +new Date();
          timer = clearTimer(timer);
        }, remaining);
      }
    }
  };
  return {
    throttle
  }
})();
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      html,
      body {
        height: 5000px;
        background-color: aquamarine;
      }
    </style>
  </head>
  <body>
    <button id="btn">立即提交</button>
    <script src="./utils.js"></script>
    <script>
      const btn = document.getElementById('btn');
      const func = () => {
        console.log(111111)
      }
      btn.onclick = utils.debounce(func, true)
      
      // window.onscroll有一个默认的触发频率,是按照浏览器的最快反应时间
      window.onscroll = utils.throttle(func, 1000);
    </script>
  </body>
</html>

总结

  • 什么时候使用节流和防抖
  1. 防抖就是不管尼搞多少次,爷就给你一次
  2. 本身触发频率非常块但是我不需要这么快的频率