记录一下throttle和debounce函数

由于最近看到了简书上关于这个的文章,想到原来在coding上看过这方面的文章所以记录总结一下。

throttle (_.throttle(func, wait, options)

这个函数的作用就是防止多次频繁的调用,规定在wait左右只能执行一次,目的是限制执行频率。
用法如下:

var throttled = _.throttle(updatePosition, 100);
$(window).scroll(throttled);

这样在滚动的时候就可以控制一下发生的频率。
underscore的源码如下:

  _.throttle = function(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};

    var later = function() {
      // 如果options.leading === false在这里重新设置 previous
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };

    var throttled = function() {
      var now = _.now();
      if (!previous && options.leading === false) previous = now;
      var remaining = wait - (now - previous);
      context = this;
      // 但是args每次都是最新的
      args = arguments;
      // 距离上次的时间已经大约wait,直接运行
      if (remaining <= 0 || remaining > wait) {
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;

      // 这个是options.leading === false的时候做第一次调用
      // 或者wait之内再调用的时候
      } else if (!timeout && options.trailing !== false) {
        timeout = setTimeout(later, remaining);
      }

      return result;
    };

    throttled.cancel = function() {
      clearTimeout(timeout);
      previous = 0;
      timeout = context = args = null;
    };

    return throttled;
  };

原理其实就是设置一个timer来控制发生的频率,用高阶函数来实现一个闭包,每次调用args都被赋予最新的值,最后调用fun的时候肯定是最新的args。保证一个wait左右只执行一个fn。
然后我们来看看的他的亲兄弟debounce,看似很相似,其实应用场景是不太一样的。
它的作用是拖延执行fun wait时间,wait内又调用fun那么再拖延wait时间,最后没有wait内调用那么执行,目的多次频繁的调用合并为一次执行,不需要中间的过程。
应用场景如 form 的提交,防止两次频繁的点击。或者,markdown编辑同时渲染显示。

var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);

这个应用只需要最后的size就好,不需要中间的多余计算。
源码如下:

  _.debounce = function(func, wait, immediate) {
    var timeout, result;

    var later = function(context, args) {
      timeout = null;
      if (args) result = func.apply(context, args);
    };

    var debounced = restArgs(function(args) {
      var callNow = immediate && !timeout;
      if (timeout) clearTimeout(timeout);
      if (callNow) {
        timeout = setTimeout(later, wait);
        result = func.apply(this, args);
      } else if (!immediate) {
        timeout = _.delay(later, wait, this, args);
      }

      return result;
    });

    debounced.cancel = function() {
      clearTimeout(timeout);
      timeout = null;
    };

    return debounced;
  };

每次调用都清掉先前的timeout,推移一个timeout。
如果immediate为true的话,立刻调用,然后wait内不可以再次调用,
可以用于form提交。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,234评论 19 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,826评论 18 399
  • 最近在研究页面渲染及web动画的性能问题,以及拜读《CSS SECRET》(CSS揭秘)这本大作。 本文主要想谈谈...
    诺奕阅读 1,142评论 0 11
  • 经常在论坛上看到很多新手妈妈求助:宝宝怎么一直睡觉不醒,会不会饿呀?不会有什么问题吧;宝宝都好几个...
    早早圆嘟嘟阅读 404评论 0 0
  • 说到我们家乡的特色小吃――红薯叶子米果,我的口水就好像要止不住地流,好想立即吃上一顿以解馋。下面我就来向大家介绍一...
    kkssb666阅读 1,053评论 0 0