理解节流 & 防抖

2,104 阅读2分钟

概念

节流 (throttle)与 防抖 (debounce)都是为了降低调用频率的一种方式。

相同点都是需要设置一个回调函数及周期时间,区别在于:

  • 防抖是在停止触发后的100ms,执行一次(在此时间段内,只要不停止触发,理论上就永远不会触发回调)

  • 节流是在不断的触发过程中,每隔100ms就执行一次。

下面简单画下流程图

节流 throttle

在高频触发回调函数时,节流操作使回调函数在每隔一段时间定期执行一次,时间间隔内再触发,不会重新执行。

核心在于让一个函数不要执行的太频繁,减少一些过快的操作。

类似于技能冷却时间 ⏱

/**
 * 节流
 * @param func 
 * @param wait 
 */
function throttle(func: Function, wait: number) {
  let timer: number = 0;
  return (...args) => {
    if (timer) { return }
    timer = window.setTimeout(() => {
      func(...args)
      timer = 0
    }, wait)
  }
}

防抖 debounce

在高频触发回调函数时,防抖操作使回调函数在一定时间间隔内,再次触发会清空定时器,并重新计时;计时结束后输出一次结果。

核心在于,在短时间内大量触发同一事件时,只会执行一次回调函数。避免把一次事件误认为多次。

/**
 * 防抖
 * @param func 
 * @param wait 
 */
function debounce(func: ()=>void, wait: number) {
  let timer: number = 0
  return (...args) => {
    clearTimeout(timer)
    timer = window.setTimeout(() => {
      func(...args)
      timer = 0; // 必须么??
    }, wait)
  }
}

tips: 注意 args 剩余参数的传递,否则执行回调函数时参数将丢失。

🌰 栗子

// 监听页面滚动条位置
const handleScrollTop = () => {
   const scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  console.log('滚动条位置:' + scrollTop);
}

// 防抖:在停止滚动100ms后,才会输出滚动条位置
window.onscroll = debounce(handleScrollTop,100)

// 节流:每隔100ms都会输出一次滚动条位置
window.onscroll = throttle(showTop,100) 

常见场景

  • 监听 scroll、mousemove 等事件 - 节流(每隔一秒计算一次位置)
  • 监听浏览器窗口 resize 操作 - 防抖(只需计算一次)
  • 键盘文本输入的验证 - 防抖(连续输入文字后发送请求进行验证,验证一次就好)
  • 提交表单 - 防抖(多次点击变为一次)

节流、防抖有时用哪个都可以,比如监听页面滚动,可以节流(每个一段时间出发一次回调),也可以防抖(用户当前这次滚动结束出发,继续滚动等待下一次触发)

React 中使用

  • Class 组件中

需要注意调用节流/防抖函数位置,在组件初始化时就绑定节流/防抖事件,否则回调函数不会被触发。

constructor(props: any) {
  super(props);
  // 注意在此绑定!!
  this.handleScroll = throttle(this.handleScroll, 100)
}
  • 在函数式组件中,由于渲染的问题使用起来需要特别注意。可以参考

React hooks 怎样做防抖?

阿里的useDebounce


参考

github.com/jashkenas/u…

github.com/lessfish/un…

linjingyi.cn/posts/7c266…

juejin.cn/post/685457…