JavaScript函数去抖动(debounce)
一、概述
在实际项目中,常常会遇到密集触发的问题,比如说用户滚动鼠标滚轮或者用户缩放或者是条件改变触发ajax请求,如果我们对于每次触发都作出相应,一方面可能会导致性能损失,另一方面其实也是做了很多无意义的事。比如说用户滚动鼠标滚轮,事实上,用户滚动几次滚轮就会触发几十次的scroll事件,我们并不需要关心用户每次滚动,我们只需要关心用户停下来滚动的那一刻,在这一刻做出相应就可以了。而用户停没停下来可以用一个很微小的间隔,比如说100ms来区分。这个过程就是经典的函数去抖动(debounce)。
二、debounce实现
debounce的实现原理很简单,主要还是依赖于定时器SetTimeout,借用闭包来实现。 也就是return一个去过抖动(debounced)的函数。这个debounced函数每次执行的时候先要判断是不是存在一个定时handle(timerID)。如果存在说明判断用户还在操作,没有停止。那么它就需要清除这个handle并从新定时。而如果你用户停止了频繁的操作后,定时器会在定时结束后自动执行你注册的响应函数。实现如下:
这个函数有一些可以改进的地方,比如说默认定时100ms,那如果用户就是想设成0,这个也会变成100,另一方面,ES6中可以给参数设置默认值,所以我们可以改成这样:
另一个可以改进的地方是,ES6中arguments或多或少已经废弃了,因为我们有了剩余运算符(...),所以这里我们可以改进如下:
三、trailing模与于leading模式
从上面的例子可以看出我们事实上是在密集触发事件的最后来真正执行一次响应函数,这也就是所谓的trailing模式。这也是大多数情况下需要用到的模式。但是有的人却希望,我在每一波密集触发事件的第一次触发就响应,那这种模式成为leading模式。二者比较形象的表示如下:
tailing模式效果:
leading模式效果:
那要实现这样的功能我们需要将前面的额函数做一些改进,如下:
四、使用案例
4.1、案例1————返回顶部
当用户滚动到底部时,页面通常需要提供一个返回顶部的按钮,那么就需要监听scroll事件,但是不应该对每次scroll事件都做响应,需要等滚动停顿下来的时候响应一下,所以可以这样:
4.2、案例2————去抖动ajax
在我做过的一个vue的项目中,某个页面有很多个选择框、按钮等,都是变化条件,每次进这个页面都会从后端拿到这些东西的值填进去,然后这个每个变化又都需要向后端发请求,请求这些参数对应的数据来绘图,因为变化条件很多,导致每次填进去的时候都会触发多次请求,如下:
这个倒还不是最严重的问题,最严重的是,在谋学情况下,因为条件填进去是有先后的,导致请求时的参数不一致,而请求的回来顺序是不可控的,所以导致,我们最后拿到的数据并不是填完完整条件后拿到的数据:
我们看到,页面渲染的数据是“当周,1周后,2周后”,然后就没有了,但我们实际的数据是这样的
这是因为这个请求最后发出,条件是完整的,但是比前面的先发出、条件不完整的请求更早回来
导致我们拿这个数据去渲染页面了,所以我们可以用debounce来去抖动一下,让它在最后密集触发完之后发一个请求,这样无效请求没了,数据也对了:
JavaScript函数节流(throttle)
一、概述
在实际项目中,常常会遇到密集触发的问题,比如说用户滚动鼠标滚轮或者用户缩放或者是条件改变触发ajax请求,如果我们对于每次触发都作出相应,一方面可能会导致性能损失,另一方面其实也是做了很多无意义的事。比如说用户滚动鼠标滚轮,事实上,用户滚动几次滚轮就会触发几十次的scroll事件。
前面我们介绍了函数去抖动(debounce),它的思想是我们并不需要关心用户每次滚动,我们只需要关心用户停下来滚动的那一刻,在这一刻做出相应就可以了。这是一个比较合理的处理方式。
然而有些人想要另外一种处理方式,那在指定的时间内,只能响应一次,它不关心是在密集事件的开始还是结束进行响应。
这种情况相较于函数去抖动(debounce)思路简单多了,就更容易实现了。
二、throttle实现
throttle实现的关键任然是setTimeout+闭包:
三、实例
我们以scroll为例来看看效果:
不用节流函数情况:
效果
可以看到scroll事件响应了10次
用节流函数情况:
可以看到scroll事件响应了1次
领取专属 10元无门槛券
私享最新 技术干货