关于underscore源码中throttle函数的疑惑?

尹光耀
  • 2.1k
_.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;
      // 如果超过了wait时间,那么就立即执行
      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;
    };
  };

如上所示,在underscore中可以传入leading为false来控制第一次是否立即执行,那么我想问一下if (!previous && options.leading === false) previous = now;这句到底是什么意思?为什么options.leading为false的时候每次都要把now赋值给previous?这样now - previous不是必然为0?这样remaining就是wait了。
但是在options.leading不为false,也就是第一次立即执行的时候,为什么previous却是上次执行的later函数里面记录的时间,这个时候now - previous肯定是大于0的,这样remaining岂不是永远小于wait?
为什么这两种情况下最后的remaining值却不一样?这是怎么处理的?

回复
阅读 1.8k
2 个回答

其实从代码上来看,当设置了option.leading=false的时候:

  1. 首次调用函数时,previous=now,即remaining=wait,所以会延迟wait时间调用
  2. 在延迟的wait时间内,previous维持在上次的now值,也就是函数调用的时间,无法调用函数
  3. 在wait时间过后,setTimeout回调later,又previous=0
  4. 下次调用函数的时候,由于previous===0,因此又有previous=now,回到步骤1

也就是在设置了option.leading=false的时候,不只是令首次函数调用被延迟,而是每一次函数调用都被延迟。

至于remaining的值,始终为要调用还需要等待的时间。函数的延迟调用方式是通过修改previous的赋值方式来控制的。

下面是官方的文档:

By default, throttle will execute the function as soon as you call it for the first time, and, if you call it again any number of times during the wait period, as soon as that period is over. If you'd like to disable the leading-edge call, pass {leading: false}

翻译一下就是:

默认情况下, throttle返回的函数在你第一次调用的时候都会尽快执行, 并且, 不管你在延迟期内调用函数多少次(都会失效), 直到延迟期结束. 如果你想要禁止首次调用, 传递参数 {leading: false}

然而我觉得这个文档很有问题。因为第一次调用也没有被禁止,而是被延迟了。后续的所有函数调用也都变成了延迟调用的形式。如果有什么不对的地方希望可以跟我交流一下。

!previous条件被你吃了?怎么会每次都为now呢?

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏