美文网首页
debounce vs throttle

debounce vs throttle

作者: vavid | 来源:发表于2020-08-20 11:49 被阅读0次

常说的函数防抖和函数节流,都是为了不频繁触发某类操作或者接口请求。
debounce(防反跳): 防抖
throttle(节流阀): 节流


debounce

举个例子:当从键盘输入内容时,监听keyUp事件,当连续不断的输入时,我们使用debounce,它的想法是这样的:可以搜了吗?哦,你还在输啊,那我再等等吧~
所以debounce的一个应用场景:搜索词联想匹配
代码实现

   /**
     * 函数防抖:返回函数连续调用时,间隔时间必须大于或等于 wait,func 才会执行
     * @param  {function} func        传入函数
     * @param  {number}   wait        表示时间窗口的间隔
     * @param  {boolean}  immediate   设置为ture时,调用触发于开始边界而不是结束边界
     * @return {function}             返回客户调用函数
     */
    debounce: function (func, wait, immediate) {
        var timeout, args, context, timestamp, result;
        var later = function () {
            // 据上一次触发时间间隔
            var last = new Date().getTime() - timestamp;

            // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
            if (last < wait && last > 0) {
                timeout = setTimeout(later, wait - last);
            } else {
                timeout = null;
                // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
                if (!immediate) {
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                }
            }
        };

        return function () {
            context = this;
            args = arguments;
            timestamp = new Date().getTime();
            var callNow = immediate && !timeout;
            // 如果延时不存在,重新设定延时
            if (!timeout) timeout = setTimeout(later, wait);
            if (callNow) {
                result = func.apply(context, args);
                context = args = null;
            }

            return result;
        };
    }

这样调用:

function handleSomething(e){
// ... do something
}
XX.debounce(handleSomething, 150, false)

throttle

举个例子:当页面上下滚动时,要根据页面高度,页面楼层导航需要吸顶或者取消吸顶时,我们使用throttle,它的想法是这样的:你别让我一直判断要不要吸顶!我很累的!我隔XXms给你判断一下吧!
所以debounce的一个应用场景:页面吸顶导航或悬浮导航
代码实现

throttle = function(func, wait, options) {
    var context, args, result;

    // setTimeout 的 handler
    var timeout = null;

    // 标记时间戳
    // 上一次执行回调的时间戳
    var previous = 0;

    // 如果没有传入 options 参数
    // 则将 options 参数置为空对象
    if (!options)
      options = {};

    var later = function() {
      // 如果 options.leading === false
      // 则每次触发回调后将 previous 置为 0
      // 否则置为当前时间戳
      previous = options.leading === false ? 0 : new Date().getTime();
      timeout = null;
      result = func.apply(context, args);
      // 这里的 timeout 变量一定是 null 了吧
      // 是否没有必要进行判断?
      if (!timeout)
        context = args = null;
    };

    // 以滚轮事件为例(scroll)
    // 每次触发滚轮事件即执行这个返回的方法
    // _.throttle 方法返回的函数
    return function() {
      // 记录当前时间戳
      var now = new Date().getTime();

      // 第一次执行回调(此时 previous 为 0,之后 previous 值为上一次时间戳)
      // 并且如果程序设定第一个回调不是立即执行的(options.leading === false)
      // 则将 previous 值(表示上次执行的时间戳)设为 now 的时间戳(第一次触发时)
      // 表示刚执行过,这次就不用执行了
      if (!previous && options.leading === false)
        previous = now;

      // 距离下次触发 func 还需要等待的时间
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;

      // 要么是到了间隔时间了,随即触发方法(remaining <= 0)
      // 要么是没有传入 {leading: false},且第一次触发回调,即立即触发
      // 此时 previous 为 0,wait - (now - previous) 也满足 <= 0
      // 之后便会把 previous 值迅速置为 now
      // ========= //
      // remaining > wait,表示客户端系统时间被调整过
      // 则马上执行 func 函数
      // @see https://blog.coding.net/blog/the-difference-between-throttle-and-debounce-in-underscorejs
      if (remaining <= 0 || remaining > wait) {
        if (timeout) {
          clearTimeout(timeout);
          // 解除引用,防止内存泄露
          timeout = null;
        }

        // 重置前一次触发的时间戳
        previous = now;

        // 触发方法
        // result 为该方法返回值
        result = func.apply(context, args);
        // 引用置为空,防止内存泄露
        // 感觉这里的 timeout 肯定是 null 啊?这个 if 判断没必要吧?
        if (!timeout)
          context = args = null;
      } else if (!timeout && options.trailing !== false) { // 最后一次需要触发的情况
        // 如果已经存在一个定时器,则不会进入该 if 分支
        // 如果 {trailing: false},即最后一次不需要触发了,也不会进入这个分支
        // 间隔 remaining milliseconds 后触发 later 方法
        timeout = setTimeout(later, remaining);
      }

      // 回调返回值
      return result;
    };
  };

这样调用:

function handleSomething(e){
// ... do something
}
XX. throttle(handleSomething, 150, false)

相关文章

网友评论

      本文标题:debounce vs throttle

      本文链接:https://www.haomeiwen.com/subject/uzairktx.html