Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

函数防抖/节流原理 #16

Open
Genluo opened this issue Aug 31, 2019 · 0 comments
Open

函数防抖/节流原理 #16

Genluo opened this issue Aug 31, 2019 · 0 comments

Comments

@Genluo
Copy link
Owner

Genluo commented Aug 31, 2019

函数防抖(debounce):函数经过固定的时间进行调用,如果在这固定的时间内重新调用函数,则会重新计算调用时间。(函数去抖就是对于一定时间段的连续的函数调用,只让其执行一次)

函数节流(throttle):函数调用的时候设定一个执行周期,当调用动作的时刻大于执行周期则执行该动作,然后进入下一个周期。(让一个函数不要执行得太频繁,减少一些过快的调用来节流)

debounce 实现代码:

两种思路如下:

  • 第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它会清除前一次的定时器并设置另一个。
  • 另一种就是记录调用时间,然后在另一个定时器函数调用的时候重新计算,然后做出下一步判断,下面就是这种方式的实现,这种为underscore实现思路。这种相比上面那种实现思路的好处在于:
    • 可以在调用前进行触发
    • 性能提高也不少,最显而易见的是基础版每此触发事件都会取消定时器,然后重新设置定时器,而 underscore 中会在一定时间后才取消定时器,重新设置定时器
// fun节流函数
// wait 间隔时间
// 是否立即执行,最后不执行,只在最开始执行一次
function debounce(func, wait = 100, immediate){
  var timeout, args, context, timestamp, result;

  function later() {
    var last = Date.now() - timestamp;

    if (last < wait && last >= 0) {
      timeout = setTimeout(later, wait - last);
    } else {
      timeout = null;
      if (!immediate) {
        result = func.apply(context, args);
        context = args = null;
      }
    }
  };

  var debounced = function(){
    context = this;
    args = arguments;
    timestamp = Date.now();
    var callNow = immediate && !timeout;
    if (!timeout) timeout = setTimeout(later, wait);
    if (callNow) {
      result = func.apply(context, args);
      context = args = null;
    }

    return result;
  };

  debounced.clear = function() {
    if (timeout) {
      clearTimeout(timeout);
      timeout = null;
    }
  };
  
  debounced.flush = function() {
    if (timeout) {
      result = func.apply(context, args);
      context = args = null;
      
      clearTimeout(timeout);
      timeout = null;
    }
  };

  return debounced;
};

throttle实现方式如下:

  • 一种实现思路就是第一次调用创建定时器,第二次调用的时候判断定时器是否执行完成,执行完成之后,然后重新新建定时器,这种方法不能保证是均匀调用的,所以我们提出第二种方法
  • 另一种实现思路算是优化吧,添加记录时间的变量,然后根据变量实现均匀调用等等,实现如下:
function throttle(fun, wait) {
  let timeout, timestamp, last;

  function setTime(...args) {
    const delayTime = last ? +new Date() - last : 0;
    return setTimeout(() => {
      last = +new Date();
      fun.apply(this, args);
      clearTimeout(timeout);
      timeout = null;
    }, wait - delayTime);
  }

  return function (...args) {
    timestamp = +new Date();
    // 考虑客户端主动更改时间(last > timestamp),因为我们封装的是立即调用函数,所以在客户端更改时间之后,应该立即调用函数,并且清除定时器
    if (!last || (timestamp - last >= wait && !timeout) || last > timestamp) {
      clearTimeout(timeout);
      timeout = null;
      last = timestamp;
      fun.apply(this, args);
    }

    if(!timeout) {
      timeout = setTime.apply(this, args);
    }
  }
}

问题:

  • 考虑客户端修改系统时间,应该马上执行函数;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant