程序员技能getWeb前端之路

记录一下throttle和debounce函数

2016-03-24  本文已影响1272人  wpzero

由于最近看到了简书上关于这个的文章,想到原来在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提交。

上一篇下一篇

猜你喜欢

热点阅读