JS

防抖(debounce)&节流(throttle)

JS

由 Whiskeyi 于 2021-10-12 发布
全文 1.3k 字, 阅读约需 6 分钟
浏览

防抖(debounce)&节流(throttle)

什么是防抖和节流?

防抖(debounce)

  当调用动作 n 毫秒后,才会执行该动作,若在这 n 毫秒内又调用此动作则将重新计算执行时间。
  理解:持续触发不执行,不触发的一段时间之后才执行。

节流(throttle)

  预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。
  理解:隔一定执行周期(时间)触发一次事件。

业务场景

debounce

1.scroll事件(资源的加载) 2.mousemove事件(拖拽) 3.resize事件(响应式布局样式) 4.keyup事件(输入框文字停止打字后才进行校验)

throttle

1.click事件(不停快速点击按钮,减少触发频次) 2.scroll事件(返回顶部按钮出现\隐藏事件触发) 3.keyup事件(输入框文字与显示栏内容复制同步) 4.减少发送ajax请求,降低请求频率

案例分析

  下面来看一个案例,我们为实现输入框文字和显示栏内容同步,给 id 为 num 的 input 对象添加监听事件,当输入框输入内容时触发监听实现,并将结果显示在 id 为 show 的 input 对象中。
  在输入框输入文字时,大量执行了操作 DOM 的函数(操作 DOM 是很耗费性能的),如图所示:
case

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
49
50
51
52
53
54
55
56
57
58
59
60
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../public/css/bootstrap.min.css" />
<title>Document</title>
<style>
.label {
color: #000;
margin-right: 20px;
}
.inline-block {
display: inline-block;
}
.container {
margin-top: 50px;
}

.section {
margin-bottom: 100px;
}
.title {
margin-bottom: 50px;
}
.boxes {
font-size: 22px;
}
</style>
</head>
<body>
<div class="container">
<h1 class="title text-center">Case</h1>
<div class="boxes">
<div class="form-group">
<label>Num</label>
<input type="text" class="form-control" id="num" />
</div>
<div class="form-group">
<label>Show</label>
<input
type="text"
class="form-control"
id="show"
disabled="disabled"
/>
</div>
</div>
</div>
<script>
var numElmt = document.getElementById("num");
function myFunction() {
console.log(numElmt.value);
document.getElementById("show").value = numElmt.value;
}
numElmt.addEventListener("input", myFunction, false);
</script>
</body>
</html>

防抖代码实现

  我们对上述代码进行防抖处理,防抖本质上是需要 Window 对象的 setTimeout()来辅助实现,延迟运行需要执行的代码,如又调用此动作则将重新计算延迟(clearTimeout),等延迟时间(delay)完毕时执行目标代码。
case2

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../public/css/bootstrap.min.css" />
<title>debounce</title>
<style>
.label {
color: #000;
margin-right: 20px;
}
.inline-block {
display: inline-block;
}
.container {
margin-top: 50px;
}

.section {
margin-bottom: 100px;
}
.title {
margin-bottom: 50px;
}
.boxes {
font-size: 22px;
}
</style>
</head>
<body>
<div class="container">
<h1 class="title text-center">Case</h1>
<div class="boxes">
<div class="form-group">
<label>Num</label>
<input type="text" class="form-control" id="num" />
</div>
<div class="form-group">
<label>Show</label>
<input
type="text"
class="form-control"
id="show"
disabled="disabled"
/>
</div>
</div>
</div>
<script>
function debounce(fn, delay) {
let timer = null; //闭包
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, delay);
};
}
var numElmt = document.getElementById("num");
function myFunction() {
console.log(numElmt.value);
document.getElementById("show").value = numElmt.value;
}
numElmt.addEventListener("input", debounce(myFunction, 1000), false);
</script>
</body>
</html>

节流代码实现

  在函数执行一次之后,让函数在指定的时间期限内不再工作,直至过了限定期限再重新执行。节流的实现方式有很多种,在此利用 setTimeout 和状态位 valid(表示当前函数是否处于工作状态)做一个简单的实现。
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../public/css/bootstrap.min.css" />
<title>throttle</title>
<style>
.label {
color: #000;
margin-right: 20px;
}
.inline-block {
display: inline-block;
}
.container {
margin-top: 50px;
}

.section {
margin-bottom: 100px;
}
.title {
margin-bottom: 50px;
}
.boxes {
font-size: 22px;
}
</style>
</head>
<body>
<div class="container">
<h1 class="title text-center">throttle</h1>
<div class="boxes">
<div class="form-group">
<label>Num</label>
<input type="text" class="form-control" id="num" />
</div>
<div class="form-group">
<label>Show</label>
<input
type="text"
class="form-control"
id="show"
disabled="disabled"
/>
</div>
</div>
</div>
<script>
function throttle(fn, delay) {
let valid = false;
return function () {
setTimeout(() => {
fn();
valid = true;
}, delay);
};
}
var numElmt = document.getElementById("num");
function myFunction() {
console.log(numElmt.value);
document.getElementById("show").value = numElmt.value;
}
numElmt.addEventListener("input", throttle(myFunction, 1000), false);
</script>
</body>
</html>

总结

  根据实际业务场景,通过对高频事件合理的利用和选择防抖(debounce)和节流(throttle)可以在一定程度上优化性能和提高用户体验。