节流与防抖(转)

节流
防抖

debounce

有两种防抖模式:

  • 延迟执行
  • 立即执行

应用场景

  • 输入搜索 - 延迟执行
  • 点击按钮搜索 - 立即执行

输入搜索

输入框中实时键入文本搜索,适合延迟执行。用户在输入完整的搜索内容后,会停止一段时间,这时候发起搜索行为。

需求:

  1. 监听到用户开始输入后,并不马上执行,等一段时间后再执行
  2. 等待时间中,有新的输入事件,重置计时器
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
39
40
41
42
43
44
45
46
47
48
/**
* start--> A{计时器是否为空}
* A--> |是| B(添加计时器:过了 等待时间 后,执行函数并清除计时器)
* A--> |否| C(重置计时器)
* B--> 结束
* @param {Function} func
* @param {Integer} wait 毫秒
*/
function debounce(func, wait = 0) {
if (typeof func !== 'function') {
throw new TypeError('debounce传入的参数必须是函数');
}

let timer = null, curThis = null, curArgs = null;

function invokeFunc() {
func.apply(curThis, curArgs);
}

function clearTimer() {
clearTimeout(timer);
timer = null;
}

function addTimer() {
timer = setTimeout(() => {
invokeFunc();
clearTimer();
}, wait)
}

function resetTimer() {
clearTimer();
addTimer();
}

function debounced(...args) {
curThis = this;
curArgs = args;
if (timer) {
resetTimer();
} else {
addTimer();
}
}

return debounced;
}

点击搜索

用户点击搜索按钮,立即执行搜索,并启动定时器,一段时间后清除定时器。一段时间内多次点击,只执行一次,忽略后面的行为,并重置定时器。

需求

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
39
40
41
42
43
44
45
/**
* start--> IF1{计时器是否为空}
* IF1--> |是| A(执行搜索)
* A--> B(添加定时器:过了 等待时间 后清除定时器)
* IF1--> |否| C(清除定时器)
* C--> B
* B--> 结束
*/
function debounce(func, wait = 0) {
if (typeof func !== 'function') {
throw new TypeError('debounce传入的参数必须是函数');
}

let timer = null, curThis = null, curArgs = null;

function addTimer() {
timer = setTimeout(() => {
clearTimer();
}, wait)
}

function clearTimer() {
clearTimeout(timer);
timer = null;
}

function invokeFunc() {
func.apply(curThis, curArgs);
}

function debounced(...args) {
curThis = this;
curArgs = args;

if (timer) {
clearTimer();
} else {
invokeFunc();
}

addTimer();
}

return debounced;
}

节流

频繁触发的事件,事件处理函数在一定的时间间隔内只执行一次。

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
/**
* start--> IF1{timer is null?}
* IF1--> |Yes| A(invoke callback)
* A--> B(add timer, remove timer after wait ms)
* B--> end
* IF1---> |No| end
* @param {Function} func
* @param {Integer} wait ms
*/
function throttle(func, wait) {
let timer = null, curThis = null, curArgs = null;

function invokeFunc() {
func.apply(curThis, curArgs);
}

function addTimer() {
timer = setTimout(() => {
clearTimeout(timer);
timer = null;
}, wait);
}

function throttled(...args) {
curThis = this;
curArgs = args;
if (timer) {
invokeFunc();
addTimer();
}
}

return throttled;
}

总结

节流和防抖类似,都能有效优化系统性能,不过使用业务场景有所区别:

  • 防抖既可用于在多次触发的事件(如文本框输入),也可用于频繁触发的事件(如滚动条)
  • 节流只用在频繁触发的事件上