throttle-vs-debounce

Stephen Cui ... 2019-08-01 14:57:50
  • SizeMe
  • Throttle
  • Debounce
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

# 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

# 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

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

# 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

# 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

# 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)

Last update: October 10, 2021 09:12
Contributors: Stephen Cui