去抖(3种场景)&节流

167 阅读1分钟
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Page Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      .btn-loading-circle:after {
        content: "\27F3";
        display: inline-block;
        animation: mrotate 1.6s linear infinite 0s;
        /*
          !!!注意:此处如不禁用,当dom元素改变时,去抖缓存值会失效
        */
        pointer-events: none;
      }
      @keyframes mrotate {
        100% {
          transform: rotate(1turn);
        }
      }
      .disabled-font-color-gray {
        color: #cecece !important;
        /*
          !!!注意:此处如不禁用会被重复触发点击,因为当dom元素改变时,去抖缓存值失效
        */
        pointer-events: none;
      }
    </style>
  </head>
  <body>
    <h1>去抖:异步,立即,延迟</h1>
    <h2>去抖:1.异步执行(触发请求的点击)</h2>
    <h3>单个异步操作</h3>
    <button id="btnSingle">无状态变化</button>
    <button id="btnSingleLoading">带加载中状态</button>
    <h3>多个异步操作</h3>
    <button id="btnMultiple">无状态变化(如验证码倒计时)</button>
    <button id="btnMultipleLoading">带加载中状态(如验证码倒计时)</button>

    <h2>去抖:2.立即执行(触发动画或大量计算的操作,如转盘的点击旋转按钮)</h2>
    <button id="btnDebounceImmediate">去抖:2.立即执行</button>
    <a href="'http://demowap.lihaipeng.net/rotary/arr">线上示例</a>
    <h2>去抖:2.1 立即执行+异步(触发请求的点击,兼容返回时间超过间隔时间)</h2>
    <button id="btnDebounceImmediateAsync">去抖:2.1 立即执行+异步</button>
    <button id="btnDebounceImmediateAsyncDelay">
      去抖:2.1 立即执行+异步,超过间隔时间
    </button>
    <h2>去抖:3.延迟执行(模糊搜索)</h2>
    <input id="inputDebounce" />
    <a href="'http://demoweb.lihaipeng.net/rotary/">线上示例</a>

    <h1>节流:onresize&onscroll</h1>

    <script>
      // 去抖:异步,立即,延迟
      // 去抖:1.异步执行(触发请求的点击)
      // 单个异步操作
      //  无状态变化
      const btnSingle = document.getElementById("btnSingle");
      btnSingle.onclick = debounceAsync(async () => {
        console.log("btnSingle");
        await requestApiMock();
      });
      //  带加载中状态
      const btnSingleLoading = document.getElementById("btnSingleLoading");
      btnSingleLoading.onclick = debounceAsync(async (e) => {
        console.log("btnSingleLoading");
        const self = e.target;
        self.classList.add("btn-loading-circle");
        await requestApiMock();
        self.classList.remove("btn-loading-circle");
      });

      // 多个异步操作
      //  无状态变化(如验证码倒计时)
      const btnMultiple = document.getElementById("btnMultiple");
      btnMultiple.onclick = debounceAsync(async (e) => {
        console.log("btnMultiple");
        const self = e.target;
        await requestApiMock();
        await countdownSeconds((seconds) => {
          self.innerHTML = seconds === 0 ? "重新发送" : seconds + "s";
        }, 3);
      });
      //  带加载中状态(如验证码倒计时)
      const btnMultipleLoading = document.getElementById("btnMultipleLoading");
      btnMultipleLoading.onclick = debounceAsync(async (e) => {
        console.log("btnMultipleLoading");
        const self = e.target;
        self.classList.add("btn-loading-circle");
        const data = await requestApiMock({ success: true });
        self.classList.remove("btn-loading-circle");
        if (data.success === true) {
          self.classList.add("disabled-font-color-gray");
          await countdownSeconds((seconds) => {
            self.innerHTML = seconds === 0 ? "重新发送" : seconds + "s";
          }, 3);
          self.classList.remove("disabled-font-color-gray");
        }
      });

      // 去抖:1.异步
      function debounceAsync(func) {
        let isDisable = false;
        return async function () {
          if (isDisable === true) return;
          isDisable = true;
          await func.apply(this, arguments);
          isDisable = false;
        };
      }
      // 倒计时读秒
      function countdownSeconds(setSeconds, totalSecond = 60) {
        return new Promise((resolve) => {
          let seconds = totalSecond;
          setSeconds(seconds);
          seconds--;
          const timer = setInterval(() => {
            setSeconds(seconds);
            if (seconds === 0) {
              clearInterval(timer);
              resolve();
            } else {
              seconds--;
            }
          }, 1000);
        });
      }

      // 去抖:2.立即执行(触发动画或大量计算的操作,如转盘的点击旋转按钮)
      const btnDebounceImmediate = document.getElementById(
        "btnDebounceImmediate"
      );
      btnDebounceImmediate.onclick = debounceImmediate(() => {
        console.log("debounceImmediate");
      });
      // 去抖:立即执行(按钮连点请求)
      function debounceImmediate(func, wait = 2000) {
        let previous = 0;
        return function () {
          const now = +new Date();

          const remaining = previous + wait - now;
          if (previous === 0 || remaining <= 0) {
            previous = now;
            func.apply(this, arguments);
          }
        };
      }
      // 去抖:2.1 立即执行+异步(触发请求的点击,兼容返回时间超过间隔时间)
      const btnDebounceImmediateAsync = document.getElementById(
        "btnDebounceImmediateAsync"
      );
      const btnDebounceImmediateAsyncDelay = document.getElementById(
        "btnDebounceImmediateAsyncDelay"
      );
      btnDebounceImmediateAsync.onclick = debounceImmediateAsync(async (e) => {
        console.log("btnDebounceImmediateAsync");
        const self = e.target;
        self.classList.add("btn-loading-circle");
        await requestApiMock();
        self.classList.remove("btn-loading-circle");
      });
      btnDebounceImmediateAsyncDelay.onclick = debounceImmediateAsync(async (e) => {
        console.log("btnDebounceImmediateAsyncDelay");
        const self = e.target;
        self.classList.add("btn-loading-circle");
        await requestApiMock({},4000);
        self.classList.remove("btn-loading-circle");
      });
      // 去抖:立即执行+异步
      function debounceImmediateAsync(func, wait = 2000) {
        let previous = 0;
        let isDisable = false;
        return async function () {
          if (isDisable === true) return;

          const now = +new Date();
          const remaining = previous + wait - now;
          if (previous === 0 || remaining <= 0) {
            isDisable = true;
            await func.apply(this, arguments);
            isDisable = false;
            previous = now;
          }
        };
      }

      // 去抖:3.延迟执行(模糊搜索)
      const inputDebounce = document.getElementById("inputDebounce");
      inputDebounce.oninput = debounce(async (e) => {
        const self = e.target;
        console.log("inputDebounce", self.value);
        await requestApiMock();
      });
      // 去抖:延迟执行,最后一次推迟wait(模糊搜索框输入过程中)
      function debounce(func, wait = 1500) {
        let timer = null;
        return function () {
          if (timer) clearTimeout(timer);
          timer = setTimeout(() => {
            func.apply(this, arguments);
            timer = null;
          }, wait);
        };
      }

      // 节流:onresize&onscroll
      window.onresize = throttle(() => console.log("throttle onresize"), 2000);
      window.onscroll = throttle(() => console.log("throttle onscroll"), 2000);
      // window.onresize = () => console.log(123)
      // 节流:立即执行+延迟执行,最后一次保持间隔执行(浮动轴事件,窗口大小变动)
      function throttle(func, wait = 1000) {
        let previous = 0;
        let timer = null;
        return function () {
          const now = +new Date();
          const remaining = previous + wait - now;
          if (previous === 0 || remaining <= 0) {
            previous = now;
            func.apply(this, arguments);
          } else if (!timer) {
            timer = setTimeout(() => {
              clearTimeout(timer);
              timer = null;
              func.apply(this, arguments);
            }, remaining);
          }
        };
      }

      async function requestApiMock(data = {}, delay = 1500) {
        return new Promise(async (resolve) => {
          // mock
          console.log("requestApiMock start");
          setTimeout(() => {
            resolve(data);
            console.log("requestApiMock end");
          }, delay);
          return;

          const response = await getWalletInfo();
          console.log("response---:", response);
          resolve(response);
        });
      }
    </script>
  </body>
</html>