Roll your own 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
function debounce(fn: Function, interval: number) {
let timer: any = null;
return function () {
//之所以这里return函数,是为了让timer的作用于扩展,能保证多个function受同一个timer影响
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => fn(), interval);
};
}

//这里的逻辑是,拿到了debouce方法返回的函数引用,然后调用这个函数引用3次,那只有第一次进去的时候才会开始timer
const log = debounce(() => console.log(1), 2000);
log();
log();

//继续理解下面的逻辑,为什么log2和log1打印的时候不会共用一个timer,原因是因为下面调用debounce的时候返回的不是同一个函数引用,所以是隔离的
const log2 = debounce(() => console.log(2), 2000);
log2();
log2();
log2();

//output:
// 1
// 2

同理,继续思考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
function throttle(fn: Function, interval: number): Function {
let timer: any = null;
return function () {
if (!timer) timer = setTimeout(() => {
fn();
timer = null;//这里要注意把timer最后设置为null,不然只有第一次函数会执行,再往后就不会执行了,可以注释掉这行,看第一个log的运行逻辑
}, interval)
return;
}
}

//这个log可以看到没有timer设置为null的样子
const log = throttle(() => console.log('hit'), 1000);
log(); // 1s 后打印 'hit'
setTimeout(log, 1500);


const fn = () => { console.log(1) }
const log2 = throttle(fn, 2000);
const log3 = throttle(() => console.log(2), 3000);
log2();
log2();
log3();
log3();

//output:
// hit
// 1
// hit
// 2

在debounce和throttle的case里,去理解闭包在拉长变量作用域的作用,timer锁在返回函数的作用域里,延长它的生命周期,如果没有闭包的情况下,timer会在debounce和throttle函数在被调用以后立即被销毁,在下一次调用的时候就没法拿到上一次的timer定时器的引用了
有了闭包,就等于可以记忆timer了,从而实现只执行最后一次固定时间只执行一次的效果