函数的节流和防抖

防抖

前段时间用echarts画了个图,折线-饼图联动的,数据是从阿里云sls的api查询出来的,每5秒一个采样点,3天的数据就有5*12*60*24*3 = 259200个数据。图大概长这样。。

image.png

关键代码是一段事件函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
this.instance.on('updateAxisPointer', (event) => {
let xAxisInfo = event.axesInfo[0];
if (xAxisInfo) {
let dimension = xAxisInfo.value + 1;
this.instance.setOption({
series: {
id: 'pie',
label: {
formatter: '{b}: ({d}%)'
},
encode: {
value: dimension,
tooltip: transferToFlow(dimension)[0] + transferToFlow(dimension)[1]
}
}
});
}
});

这个事件监听函数在用户移动鼠标的时候会频繁触发,造成页面卡顿,图也会失去响应。一开始没想到防抖的方法,通过拟合的方式精简了一部分数据,把5秒的采样间隔取到了30秒级,卡顿的情况有了一些缓解,但还是治标不治本,并且在低配机器上仍然卡死。于是这次用防抖的方式尝试再做一次优化。

1
2
3
4
5
6
7
8
9
10
// 简陋的防抖函数。。
let debounce = (fn, delay) => {
let timer
return (e) => {
clearTimeout(timer)
timer = setTimeout( () => {
fn(e)
}, delay)
}
}

用了大量箭头函数免去了this指针丢失的问题。否则还得保存指针apply一次。。

防抖函数的原理在于,每次调用这个函数的时候,都将取消上一次的定时器,造成用户的行为永远也不会被触发,直到用户停止触发这个函数,timer定时器不再被清除,才会在delay时间之后执行一个动作。这样做的好处在于避免了重复触发带来的性能开销,坏处在于无限的取消用户的操作,并且能感觉到响应的迟滞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 加了个防抖函数,延迟200响应事件。。
this.instance.on('updateAxisPointer', debounce((event) => {
let xAxisInfo = event.axesInfo[0];
if (xAxisInfo) {
let dimension = xAxisInfo.value + 1;
this.instance.setOption({
series: {
id: 'pie',
label: {
formatter: '{b}: ({d}%)'
},
encode: {
value: dimension,
tooltip: transferToFlow(dimension)[0] + transferToFlow(dimension)[1]
}
}
});
}
},200));

效果还是立竿见影的,鼠标移动时事件触发的次数极大减少,低配机用户也没了卡顿的现象。

节流

节流我还没用过。。不过仿写了一个简单的节流函数。。节流的目的在于,不论函数触发的频率如何,都让它以固定的速度执行,因此需要记下上次执行的时间,如果已经过了threshold时间那么触发一次,并更新上一次执行的时间,否则清除计时器,并重新设定一个计时器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 简单的节流函数
let throttle = (fn, threshold) => {
let last
let timer
return (...args) => {
let now = +new Date()
if (last && now < last + threshold) {
clearTimeout(timer)
timer = setTimeout( () => {
last = now
fn(...args)
}, threshold)
} else {
last = now
fn(...args)
}
}
}
0%