throttle-vs-debounce
Stephen Cui ... 2019-08-01 14:57:50 About 3 min
# Throttle vs Debounce
# What is Debounce?
Debounce 简译为去抖(去跳), 在延迟m毫秒内执行一次fn, 如果连续操作多次(每次时间间隔小于delay时长), 则只执行最后一次操作一次。
/**
*
* @param fn {Function} 实际要执行的函数
* @param delay {Number} 延迟时间,也就是阈值,单位是毫秒(ms)
*
* @return {Function} 返回一个函数,这个函数会在一个时间区间结束后的 delay 毫秒时执行 fn 函数
*/
function debounce(fn, delay) {
var timer
return function () {
var context = this
var args = arguments
// if m < delay,则清除上次"待执行"的timer,并从新CountDown新的倒计时
// if m > delay,则清除上次"已执行过"的timer,并从新CountDown新的倒计时
clearTimeout(timer)
// 再过 delay 毫秒就执行 fn
timer = setTimeout(function () {
fn.apply(context, args)
}, delay)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Why we need throttle?
Throttle 简译为节流,在一定时间threshold内,至多一次待执行的函数func。在threshold时间内的多次操作, 则只执行threshold的第一次操作,其余抛弃
/**
*
* @param fn {Function} 实际要执行的函数
* @param threshold {Number} 执行间隔,单位是毫秒(ms)
*
* @return {Function} 返回一个“节流”函数
*/
function throttle(fn, threshold) {
// 记录上次执行的时间
var last
// 定时器
var timer
// 默认间隔为 250ms
threshold || (threshold = 250)
// 返回的函数,每过 threshold 毫秒就执行一次 fn 函数
return function () {
// 保存函数调用时的上下文和参数,传递给 fn
var context = this
var args = arguments
var now = +new Date()
// 如果距离上次执行 fn 函数的时间小于 threshold,那么就放弃
// 执行 fn,并重新计时
if (last && now < last + threshold) {
clearTimeout(timer)
// 保证在当前时间区间结束后,再执行一次 fn
timer = setTimeout(function () {
last = now
fn.apply(context, args)
}, threshold)
// 在时间区间的最开始和到达指定间隔的时候执行一次 fn
} else {
last = now
fn.apply(context, args)
}
}
}
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
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
# Application Scene
Throttle 之于 scroll
function log( event ) {
console.log( $(window).scrollTop(), event.timeStamp );
};
// 控制台记录窗口滚动事件,触发频率比你想象的要快
$(window).scroll( log );
// 控制台记录窗口滚动事件,每250ms最多触发一次
$(window).scroll( _.throttle( log, 250 ) );
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Debounce 之于 input
function ajax_lookup( event ) {
// 对输入的内容$(this).val()执行 Ajax 查询
};
// 字符输入的频率比你预想的要快,Ajax 请求来不及回复。
$('input:text').keyup( ajax_lookup );
// 当用户停顿250毫秒以后才开始查找
$('input:text').keyup( _.debounce( ajax_lookup, 250 ) );
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# Advance Analysis
``
# Debounce from lodash
// https://github.com/lodash/lodash/blob/master/debounce.js
function debounced(...args) {
const time = Date.now()
const isInvoking = shouldInvoke(time)
lastArgs = args
lastThis = this
lastCallTime = time
if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime)
}
if (maxing) {
// Handle invocations in a tight loop.
timerId = startTimer(timerExpired, wait)
return invokeFunc(lastCallTime)
}
}
if (timerId === undefined) {
timerId = startTimer(timerExpired, wait)
}
return result
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Throttle from lodash
// https://github.com/lodash/lodash/blob/4.17.15-npm/throttle.js
// 增加了options参数,增加了leading和trailing参数
function throttle(func, wait, options) {
var leading = true,
trailing = true;
if (typeof func != 'function') {
throw new TypeError(FUNC_ERROR_TEXT);
}
if (isObject(options)) {
leading = 'leading' in options ? !!options.leading : leading;
trailing = 'trailing' in options ? !!options.trailing : trailing;
}
return debounce(func, wait, {
'leading': leading,
'maxWait': wait,
'trailing': trailing
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Reference
JQuery Post for Throttle/Debounce (opens new window) Sample Throttle Code (opens new window) Throttle vs Debounce源码解析 (opens new window) Throttle.js from Lodash (opens new window)