avatar

事件的节流和防抖

有些浏览器事件可以在短时间内快速触发多次,比如调整窗口大小或向下滚动页面。例如,监听页面窗口滚动事件,并且用户持续快速地向下滚动页面,那么滚动事件可能在 3 秒内触发数千次,这可能会导致一些严重的性能问题。

如果在面试中讨论构建应用程序,出现滚动、窗口大小调整或按下键等事件请务必提及 防抖(Debouncing) 和 函数节流(Throttling)来提升页面速度和性能。这两兄弟的本质都是以闭包的形式存在。通过对事件对应的回调函数进行包裹、以自由变量的形式缓存时间信息,最后用 setTimeout 来控制事件的触发频率。

Throttle 第一个人说了算

throttle 的主要思想:在某段时间内,不管你触发了多少次回调,都只认第一次,并在计时结束时给予响应。

现在就来实现一个简单的 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
/**
* generate a throttling function
*
* @param {Function} fn The callback of event
* @param {Number} interval time interval
*/
function throttle(fn, interval) {
var last = 0

return function() {

var context = this

var args = arguments

var now = +new Date()

if (now - last >= interval) {
last = now
fn.apply(context, args)
}
}
}
var betterScroll = throttle(function() {
console.log('滚动事件触发')
}, 1000)

document.addEventListener('scroll', betterScroll)

Debounce 最后一个参赛者说了算

Debounce 的主要思想:我会等你到底。在某段时间内,不管你触发了多少次回调,我都只认最后一次。

现在就来实现一个简单的 Debounce

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
/**
* generate a debounce function
*
* @param {Function} fn The callback of event
* @param {Number} delay time interval
*/
function debounce(fn, delay) {
var timer = null

return function () {

var context = this

var args = arguments

if (timer) {
clearTimeout(timer)
}
// 设立新定时器
timer = setTimeout(function () {
fn.apply(context, args)
}, delay)
}
}

// 用debounce来包装scroll的回调
const betterScroll = debounce(function() {
console.log('滚动事件触发')
}, 1000)

document.addEventListener('scroll', betterScroll)

用 Throttle 来优化 Debounce

debounce 的问题在于它“太有耐心了”。试想,如果用户的操作十分频繁——他每次都不等 debounce 设置的 delay 时间结束就进行下一次操作,于是每次 debounce 都为该用户重新生成定时器,回调函数被延迟了不计其数次。频繁的延迟会导致用户迟迟得不到响应,用户同样会产生“这个页面卡死了”的观感。

为了避免弄巧成拙,我们需要借力 throttle 的思想,打造一个“有底线”的 debounce——等你可以,但我有我的原则:delay 时间内,我可以为你重新生成定时器;但只要delay的时间到了,我必须要给用户一个响应。这个 throttle 与 debounce “合体”思路,已经被很多成熟的前端库应用到了它们的加强版 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
/**
* generate a throttle function
*
* @param {Function} fn The callback of event
* @param {Number} delay time interval
*/
function throttle(fn, delay) {
var last = 0, timer = null

return function () {

var context = this

var args = arguments

var now = +new Date()

if (now - last < delay) {
clearTimeout(timer)

timer = setTimeout(function() {
last = now
}, delay)
} else {
last = now

fn.apply(context, args)
}
}
}
var betterScroll = throttle(function() {
console.log('滚动事件触发')
}, 1000)

document.addEventListener('scroll', betterScroll)
文章作者: Kwin
文章链接: http://kw1n.cn/2020/03/25/20190929-事件的节流和防抖/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Kwin 's Blog
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论