JS 防抖和节流
针对触发频率太高和响应很慢的问题
造成这个问题的主要原因是程序没有对事件响应做一定的限制,如果在短时间内触发频率很高,那么响应事件也会堆积很多,特别是当事件包含了网络请求,会一直向服务器发送请求,所以会希望程序不要频繁的触发响应事件。
实际应用场景
- 监听短时间内重复触发率很高的事件,如滚动条滚动事件
onscroll,窗口大小改变onresize等。
防抖(debounce)
基本策略是当事件触发时,设定一个时间周期延迟执行响应(在 JS 中可以当成是用setTimeout函数封装响应),如果在周期内响应又被触发,则重置时间周期(clearTimeout),直到周期结束,执行响应。
防抖函数有两种版本,一种是基本策略的实现,会延迟一个时间周期再执行响应事件;另一种是立即执行响应事件,再设定时间周期,如果在周期内有响应事件触发,不执行动作,且 reset 周期。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @description: 封装防抖函数,基本策略
* @param callback{function} 事件的回调函数
* @param cycle{num} 防抖的时间间隔,默认500毫秒
* @return: {function} 返回可执行函数
*/
const debounce = function (callback, cycle = 500) {
let timer = null;
const debounceFunc = function () {
let _this = this,
args = arguments;
timer && clearTimeout(timer);
timer = setTimeout(() => {
callback.apply(_this, args);
}, cycle);
};
return debounceFunc;
};
let fun = debounce(x => console.warn(x + 1), 1000);
fun(10);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @description: 防抖函数,立即执行
* @param callback{function} 事件回调
* @param cycle{num} 间隔时间
* @return: {function} 返回函数
*/
const debounce = function (callback, cycle) {
let timer;
const debounceFunc = () => {
let _this = this,
args = arguments;
timer && clearTimeout(timer);
let call = !timer;
timer = setTimeout(() => {
timer = null;
}, cycle);
if (call) {
callback.apply(_this, args);
}
};
return debounceFunc;
};
节流(throttle)
策略是在固定的时间周期内,只会执行响应事件一次;可以看到与防抖的区别就是,防抖会重置每次事件的间隔时间,如果连续触发,会导致第一次和第二次执行的间隔变长,而节流第一次和第二次执行的时间间隔是固定的。
节流的实现有两个版本,使用时间戳或者定时器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* @description: 节流函数,时间戳
* @param callback{function}
* @param cycle{num}
* @return: {function}
*/
const throttle = function (callback, cycle) {
let previous = 0;
return function () {
let now = Date.now();
let _this = this,
args = arguments;
if (now - previous > cycle) {
callback.apply(_this, args);
previous = now;
}
};
};
/**
* @description: 节流 定时器
* @param callback{function}
* @param cycle{num}
* @return: {function}
*/
const throttle = function (callback, cycle) {
let timer;
return function () {
let _this = this,
args = arguments;
if (!timer) {
timer = setTimeout(() => {
timer = null;
callback.apply(_this, args);
}, cycle);
}
};
};
区别还是比较明显的,定时器版本会在每次间隔时间的最后执行响应函数,而时间戳会在一开始就执行。
总结
防抖和节流各有特点,使用时还是要针对实际场景来应用。如果事件触发频率高,但是有一段真空期(有暂停),比如防止表单重复提交,可以使用防抖,因为肯定不会一直不间断的点提交;如果事件触发是连续的,那么可以使用节流,因为防抖会延长两次执行的时间间隔,还可能导致事件只会执行一次。