从underscore库中学习throttle 节流

463 阅读2分钟

这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战

前言

前文讲到两种实现版本的节流,这里的高配版本主要将把这两种版本进行优势互补,合并成一个节流函数,通过传参控制具体是要第一时间执行、或者最后一次执行,又或者两者兼得这三种情况

高配节流实现分析

使用时间戳可以让节流函数立即执行,使用setTimeout可以让节流函数延迟执行,通过变量控制即可以达到以上需求

通过高配版的节流 ,达到配置以下三种情况:

  • 第一次执行,末尾不执行
  • 第一次不执行,末尾执行
  • 第一次执行,末尾执行

首先还是介绍一下实例应用背景:有一个div,在div上移动触发回调函数listenMoveOn,还是这么一个简单的例子,具体还是通过代码去实现看看,详细内容通过注释体现

代码实现

<style>
    div {
        background-color: #666;
        height: 300px;
    }
</style><div></div><script>
    let dom = document.getElementsByTagName("div")[0];
    dom.onmousemove = throttle(listenMoveOn, 200);
    
    function listenMoveOn() {
        // 监听的回调做一些事情 
    }
    
    /**
     *
     * @param {function} fn - 回调函数
     * @param {number} wait - 等待时间
     * @param {obejct} options - 配置三种情况
     *   options = {leading: true, trailing: false}; leading 控制第一次是否执行; trailing 控制最后一次是否执行
     */
    function throttle(fn, wait, options) {
       /**
        *
        * @param timer - 定时器
        * @param now - 当前时间
        * @param context - 保存上下文使用
        * @param args - 保存事件参数使用
        */
        let timer, now, context, args;
        old = 0
        
        // 设置默认情况(第一次执行,最后一次不执行)
        if (!options) options = {leading: true, trailing: false} 
​
        const later = () => {
            // 这里重置old 是为了避免leading、trailing同时为true与时间戳那边相互影响,这一步相当于清除了时间戳那边的操作
            old = now
            
            clearTimeout(timer)
            timer = null
            fn.apply(context, args)
        }
​
        return function () {
            // 同防抖一样,需要闭包保存相关变量
            now = +new Date()
            context = this
            args = arguments
            
            // 配置 leading 走时间戳管理节流
            if (options.leading && now - old > wait) {
                // 这里重置定时器 是为了避免leading、trailing同时为true时与定时器那边相互影响,这一步相当于清除了定时器那边的操作
                if (timer) {
                    clearTimeout(timer)
                    timer = null
                }
                
                old = now
                fn.apply(context, args)
            } 
            // 配置 trailing 走定时器管理节流
            if (options.trailing && !timer) {
                timer = setTimeout(later, wait)
            }
        }
​
    }
</script>

undercore中的throttle

最后还是欣赏一下undercore中的throttle源码

  // Returns a function, that, when invoked, will only be triggered at most once
  // during a given window of time. Normally, the throttled function will run
  // as much as it can, without ever going more than once per `wait` duration;
  // but if you'd like to disable the execution on the leading edge, pass
  // `{leading: false}`. To disable execution on the trailing edge, ditto.
  _.throttle = function (func, wait, options) {
    var context, args, result;
    var timeout = null;
    var previous = 0;
    if (!options) options = {};
      
    var later = function () {
      // 定时器方式
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function () {
      var now = _.now();
      if (!previous && options.leading === false) previous = now;
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // 时间戳方式
      if (remaining <= 0 || remaining > wait) {
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };

总结

防抖节流已经全部讲完,通过深入学习也算有所收获啦,以前只知道调用没详细了解原理,这回彻底搞懂了 😄 爱了爱了

防抖节流文章目录: