手写节流函数

70 阅读1分钟

手写节流函数

将自己的理解和知识点写在代码注释中.(习惯了)

throttle-v1-基本实现

function throttle(fn, interval = 1000) {
  // 1.记录上一次的开始时间
  let lastTime = 0

  const _throttle = function(...args) {
    // 2.获取每次触发的时间
    const nowTime = new Date().getTime()
    console.log('现在时间:', nowTime);
    // 3.剩余时间
    const remainTime = interval - (nowTime - lastTime)
    console.log('剩余时间:', remainTime);
    if(remainTime <= 0) {
      fn.apply(this, args)
      // 4.保留上次触发的时间
      lastTime = nowTime
    }
  }
  return _throttle
}

throttle-v2-leading实现

// lead头部,默认执行
function throttle(fn, interval = 1000, options = { leading: true, trailing: false }) { 
  // 1.记录上一次的开始时间
  let lastTime = 0
  const { leading, trailing } = options
  const _throttle = function(...args) {
    // 2.获取每次触发的时间
    const nowTime = new Date().getTime()
    // 2.1对第一次输入要不要执行
    if(!lastTime && !leading) {
      lastTime = nowTime // (nowTime - lastTime)为0,第一次进入等待interval后执行
    }

    // 3.剩余时间
    const remainTime = interval - (nowTime - lastTime)
    if(remainTime <= 0) {
      fn.apply(this, args)
      // 4.保留上次触发的时间
      lastTime = nowTime
    }
  }
  return _throttle
}

throttle-v3-trailing实现

// trail尾部,默认不执行 
function throttle(fn, interval = 1000, options = { leading: true, trailing: false }) {
  // 1.记录上一次的开始时间
  let lastTime = 0
  let timer = null

  const { leading, trailing } = options
  const _throttle = function(...args) {
    // 2.获取每次触发的时间
    const nowTime = new Date().getTime()
    // 2.1对第一次输入要不要执行
    if(!lastTime && !leading) {
      lastTime = nowTime // (nowTime - lastTime)为0,第一次进入等待interval后执行
    }

    // 3.剩余时间
    const remainTime = interval - (nowTime - lastTime)
    if(remainTime <= 0) {

      if(timer){
        clearTimeout(timer)
        timer = null
      }

      fn.apply(this, args)
      // 4.保留上次触发的时间
      lastTime = nowTime
      
      return // 刚开始执行或时间刚好等于interval时,没有必要加定时器,不执行下面的代码
    }

    if(trailing && !timer) { // timer为null会进去
      timer = setTimeout(() => {
        timer = null
        lastTime = 0
        fn.apply(this, args)
      }, remainTime);
    }

  }
  return _throttle
}

throttle-v4-取消功能

// trail尾部,默认不执行 
function throttle(fn, interval = 1000, options = { leading: true, trailing: false }) {
  // 1.记录上一次的开始时间
  let lastTime = 0
  let timer = null

  const { leading, trailing } = options
  const _throttle = function(...args) {
    // 2.获取每次触发的时间
    const nowTime = new Date().getTime()
    // 2.1对第一次输入要不要执行
    if(!lastTime && !leading) {
      lastTime = nowTime // (nowTime - lastTime)为0,第一次进入等待interval后执行
    }

    // 3.剩余时间
    const remainTime = interval - (nowTime - lastTime)
    if(remainTime <= 0) {

      if(timer){
        clearTimeout(timer)
        timer = null
      }

      fn.apply(this, args)
      // 4.保留上次触发的时间
      lastTime = nowTime
      
      return // 刚开始执行或时间刚好等于interval时,没有必要加定时器,不执行下面的代码
    }

    if(trailing && !timer) { // timer有值
      timer = setTimeout(() => {
        timer = null
        lastTime = 0
        fn.apply(this, args)
      }, remainTime);
    }

  }

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

  return _throttle
}

throttle-v5-函数返回值

// trail尾部,默认不执行 
function throttle(fn, interval = 1000, options = { leading: true, trailing: false }, resultCallback) {
  // 1.记录上一次的开始时间
  let lastTime = 0
  let timer = null

  const { leading, trailing } = options
  const _throttle = function(...args) {
    // 2.获取每次触发的时间
    const nowTime = new Date().getTime()
    // 2.1对第一次输入要不要执行
    if(!lastTime && !leading) {
      lastTime = nowTime // (nowTime - lastTime)为0,第一次进入等待interval后执行
    }

    // 3.剩余时间
    const remainTime = interval - (nowTime - lastTime)
    if(remainTime <= 0) {

      if(timer){
        clearTimeout(timer)
        timer = null
      }

      const result = fn.apply(this, args)
      if(result && resultCallback && typeof resultCallback === 'function'){
        resultCallback(result)
      }

      // 4.保留上次触发的时间
      lastTime = nowTime
      
      return // 刚开始执行或时间刚好等于interval时,没有必要加定时器,不执行下面的代码
    }

    if(trailing && !timer) { // timer有值
      timer = setTimeout(() => {
        timer = null
        lastTime = 0
        const result = fn.apply(this, args)
        if(result && resultCallback && typeof resultCallback === 'function'){
          resultCallback(result)
        }
      }, remainTime);
    }

  }

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

  return _throttle
}

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>
  </head>
  <body>
    <input type="text" placeholder="请输入商品名~" />
    <button id="btn">取消</button>

    <!-- <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script> -->
    <!-- <script src="./06_throttle-v1-基本实现.js"></script> -->
    <script src="./07_throttle-v2-leading实现.js"></script>
    <!-- <script src="./08_throttle-v3-trailing实现.js"></script> -->
    <!-- <script src="./09_throttle-v4-取消功能.js"></script> -->
    <!-- <script src="./10_throttle-v5-函数返回值.js"></script> -->
    <script>
      const inputEl = document.querySelector("input");
      let index = 0;

      const inputFn = function (event) {
        console.log(`执行第${++index}次`, this, event);
        return "aaaaa";
      };

      // const newInputFn = _.throttle(inputFn, 2000);
      // 实现自己的节流函数
      const newInputFn = throttle(
        inputFn,
        3000,
        {
          leading: false,
          trailing: true,
        },
        (res) => {
          console.log("拿到函数的返回值:", res);
        }
      );

      inputEl.addEventListener("input", newInputFn);

      // 取消功能
      const btnEl = document.querySelector("#btn");
      btnEl.addEventListener("click", () => {
        newInputFn.cancel();
      });
    </script>
  </body>
</html>