防抖与节流

231 阅读4分钟

[TOC] 防抖与节流

实际上防抖和节流都是限制一些频繁的事件触发(window 的 resize、scroll、mousedown、mousemove、keyup、keydown),但他们还是有实质性的区别的防抖是虽然事件持续触发,但只有等事件停止触发后n秒才执行函数(如果你在时间内重新触发事件,那么时间就重新算,这是防抖的特点,可以按照这个特点选择场景)。比如生活中的坐公交,就是一定时间内,如果有人陆续刷卡上车,司机就不会开车。只有别人没刷,司机才开车。

节流是持续触发的时候,每隔n秒执行一次函数比如人的眨眼睛,就是一定时间内眨一次。这是函数节流最形象的解释

防抖与节流

防抖和节流的作用都是防止函数多次调用

节流:将多次执行变成每隔一段时间执行
防抖:将多次执行变成最后一次执行

节流: 规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。防抖: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

举个栗子

个人理解 函数防抖就是法师发技能的时候要读条,技能读条没完再按技能就会重新读条。

个人理解 函数节流就是fps游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹。

code

  • debounce

触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间

其实函数防抖的原理也非常地简单,通过闭包保存一个标记来保存 setTimeout 返回的值,每当用户输入的时候把前一个 setTimeout clear 掉,然后又创建一个新的 setTimeout,这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数了。

  • 思路

每次触发事件时都取消之前的延时调用方法

   //模拟一段ajax请求
   function ajax(content) {
     console.log('ajax request ' + content)
   }

   function debounce(fn, interval = 300) {
       let timeout = null;
       return function () {
           clearTimeout(timeout);//把前一个 setTimeout clear然后产生一个新的定时器
           timeout = setTimeout(() => {
               fn.apply(this, arguments);
           }, interval);
       };
   }

   let inputb = document.getElementById('debounce')

   let debounceAjax = debounce(ajax, 500)

   inputb.addEventListener('keyup', function (e) {
           debounceAjax(e.target.value)
       })

  • throttle

高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率

  • 思路

每次触发事件时都判断当前是否有等待执行的延时函数

     function throttle(fn) {
        let canRun = true; // 通过闭包保存一个标记
        return function () {
          if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
          canRun = false; // 立即设置为false
          setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
            fn.apply(this, arguments);
            // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
            canRun = true;
          }, 500);
        };
  }
  function sayHi(e) {
    console.log(e.target.innerWidth, e.target.innerHeight);
  }
  window.addEventListener('resize', throttle(sayHi));

异同比较

  • 相同点

    • 都可以通过使用 setTimeout 实现。
    • 目的都是,降低回调执行频率。节省计算资源。
  • 不同点

    • 函数防抖,在一段连续操作结束后,处理回调,利用clearTimeout 和 setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能。
    • 函数防抖关注一定时间连续触发的事件只在最后执行一次,而函数节流侧重于一段时间内只执行一次

总结

  • 防抖判断机制是定时器id,有个重新计算的概念
  • 节流是true/false开关有个周期的概念
  • 函数防抖和函数节流都是防止某一时间频繁触发,但是这两兄弟之间的原理却不一样。
  • 函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行。

应用场景

  • debounce

    • search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
    • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
  • throttle

    • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
    • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断

30 seconds

const debounce = (fn, ms = 0) => {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn.apply(this, args), ms);
  };
};
const throttle = (fn, wait) => {
  let inThrottle, lastFn, lastTime;
  return function() {
    const context = this,
      args = arguments;
    if (!inThrottle) {
      fn.apply(context, args);
      lastTime = Date.now();
      inThrottle = true;
    } else {
      clearTimeout(lastFn);
      lastFn = setTimeout(function() {
        if (Date.now() - lastTime >= wait) {
          fn.apply(context, args);
          lastTime = Date.now();
        }
      }, Math.max(wait - (Date.now() - lastTime), 0));
    }
  };
};

参考文档