debounce(防抖) & throttle(节流)

一、前言

想必前端工程师看到这两个单词都是十分的熟悉。这两种技术,都是为了避免短时间内大量触发同一种操作所做的努力。特别是,当这一操作代价比较大,如涉及到DOM操作或浏览器回流等行为时,会造成极大的性能损耗,严重时甚至会导致浏览器的崩溃。

二、一些错误的认识

debounce(防抖)和throttle(节流),这两种方法的原理很类似。都是利用setTimeout和闭包控制事件处理程序的触发频率。然而两种方法的具体实现方法及最终效果又有细微的不同。正因为两种方法是如此的类似,以及网上资料的混乱,加重了初学者的学习成本。

function debounce(fn, interval = 300) {
    let timeout = null;
    return function () {
        clearTimeout(timeout);
        timeout = setTimeout(() => {
            fn.apply(this, arguments);
        }, interval);
    };
}
复制代码

对于同一段代码,这三处地方却给出了完全不同的解释:AlloyTeam团队和javascript高级语言程序设计都将其标注为throttle,而包括第三篇blog在内的几乎所有其他地方都将其标注为debounce。我陷入了疑惑。本着前两者的权威性,一开始我也认为这是防抖,但是,直到我找到了这个:

其中明确指出“Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called”。因此,上述代码应该是debounce(防抖)。 而相应的,throttle(节流)对应的描述则是“Throttling enforces a maximum number of times a function can be called over time”。

三、debounce(防抖)

废话不多说,先上段代码

var debounce = function(fn, delay){
 	var timer = null;
 	return function(){
 		var context = this, args = arguments;
 		clearTimeout(timer);
 		timer = setTimeout(function(){
 			fn.apply(context, args);
 		}, delay);
 	};
 };


div.onclick = debounce(() => { console.log('点击事件') }, 1000);
复制代码

debounce的效果是:当事件被触发时,事件处理函数不会立即执行,而是会被放到setTimeout中延时delay执行;而当delay时间内再次触发事件,则前一个setTimeout生成的ID会被clearTimeout清除,同时生成新的ID。这样,只有在delay时间段内,没有新的event,事件处理函数才会真正执行。

这一方法可以用在输入框中,若是每次input事件触发时,都发送一个ajax操作,则会造成极大的性能浪费。使用debounce,则只会在用户停止输入后才会发送ajax请求。

但是这一方式也有自己的问题:如果事件一直源源不断的触发,则可能导致在很长一段时间内,事件处理程序都不会被调用。在某些场景下,这可能会导致用户体验不佳。比如说,需要为dialog弹窗增加鼠标拖动的功能,如果使用debounce的方法,则可能会导致在鼠标移动期间,弹窗一直不动,直到鼠标停止移动,弹窗会一下子跳到终止位置。这显然是不合理的。

四、throttle(节流)

function throttle(fun, delay) {
  let last, deferTimer
  return function (args) {
      let that = this
      let _args = arguments
      let now = +new Date()
      if (last && now < last + delay) {
          clearTimeout(deferTimer)
          deferTimer = setTimeout(function () {
              last = now
              fun.apply(that, _args)
          }, delay)
      }else {
          last = now
          fun.apply(that,_args)
      }
  }
}

let throttleAjax = throttle(ajax, 1000)

let inputc = document.getElementById('throttle')
inputc.addEventListener('keyup', function(e) {
  throttleAjax(e.target.value)
})
复制代码

throttle的的效果和debounce类似,也是会限制delay时间内事件的触发次数。但是,相较于debounce,throttle的好处在于当事件连续触发时,每delay时间段,事件处理函数都会执行一次。从而避免了debounce的连续触发事件,导致事件处理函数一直无法调用的极端情况。

throttle可以用在滚屏预加载,弹窗拖动等场景。这些场景的特征是,在事件节流的基础上,要求每隔一段时间,至少执行一次事件处理函数。

五、总结

debounce解决了短时间内大量事件触发的问题,只有当delay时间段内,没有新的事件触发,才会真正执行事件处理函数;但是,极端情况下,事件处理函数可能一直得不到执行。

throttle则在解决事件节流的基础上,定期执行事件处理函数。

ps:以上纯属个人理解,如有错误,欢迎大家指出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值