节流 throttle 和防抖 debounce 的用途,区别,实现及应用场景

2,623 阅读2分钟

1.用途

节流和防抖本质都是优化高频率执行代码的一种手段,例如浏览器的resizescrollkeypressmousemove等事件在触发时,都会不断的调用绑定在事件上的回调函数,会极大地浪费资源,降低前端性能,为了优化性能,这时候就可以采用节流(throttle)和防抖(debounce)来减少调用频率。

定义

  • 节流:n秒内只运行一次,若在n秒内重复触发,则只有一次生效
  • 防抖:等待n秒后执行这件事情,如果在等待的n秒内重复触发,则重新计时

举例

  • 节流:类似于游戏中的技能冷却,假设技能只能在10s内触发一次,当你使用了这个技能之后,在之后的10s内技能会进入冷却期,无法再次使用,只有等cd结束,才能再次使用。
  • 防抖:类似于moba中的回城技能,假设当你开始读条回城时,此时会有一个10s的回城计时器。如果你不做操作,回城计时器10s计时结束后,便可以回城;但如果在这个10s期间,你如果打断了回城,那回城计时器的10s等待时间便会重新计时,直到10s后才能回城。

2.代码实现

节流

const throttle = (fn, delay) => {
  let timer = null;
  return (...args) => {
    // 使用 null 作为判定条件,默认返回 false
    if (timer) return;
    // fn 写在 setTimeout 外是执行后等待 delay 才能再次触发,即先触发再等待
    // fn 写在 setTimeout 内则是等待 delay 后才能执行,之后可以再次触;即先等待再触发
    fn.call(undefined, ...args);
    timer = setTimeout(() => {
      timer = null;
    }, delay);
  };
};

防抖

const debounce = (fn, delay) => {
  let timer = null;
  return (...args) => {
    if (timer !== null) {
      clearTimeout(timer);  // cd中再次调用 fn 时清空计时器
    }
    timer = setTimeout(() => {
      fn.call(undefined, ...args);   // delay 后调用 fn 并清空计时器
      timer = null;
    }, delay);
  };
};

3.区别

相同点

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

不同点

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

4.应用场景

节流

  • 滚动加载,加载更多或滚到底部监听
  • 搜索框,搜索联想功能
  • 验证码的发送按钮

防抖

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测
  • 窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染