debounce & throttle
- lodash
- API
_.debounce(func, [wait=0], [options={}]) func (Function): 要防抖动的函数。 [wait=0] (number): 需要延迟的毫秒数。 [options={}] (Object): 选项对象。 [options.leading=false] (boolean): 指定在延迟开始前调用,默认false。 [options.maxWait] (number): 设置 func 允许被延迟的最大值。 [options.trailing=true] (boolean): 指定在延迟结束后调用,默认true。 _.throttle(func, [wait=0], [options=]) func (Function): 要节流的函数。 [wait=0] (number): 需要节流的毫秒。 [options=] (Object): 选项对象。 [options.leading=true] (boolean): 指定调用在节流开始前。 [options.trailing=true] (boolean): 指定调用在节流结束后。
- API
- underscore
- API
_.debounce(func, wait, [immediate]) func (Function): 要节流的函数。 [wait=0] (number): 需要节流的毫秒。 [immediate=false] (number): 第一次是否立即执行 _.throttle(func, wait, [options]) func (Function): 要节流的函数。 [options=] (Object): 选项对象。 [options.leading=true] (boolean): 指定调用在节流开始前。 [options.trailing=true] (boolean): 指定调用在节流结束后。
- API
debounce
- 为什么需要使用
- 使用场景:
- mouse move
- 在用户输入完成时进行字符串校验
- size/scroll的触发统计事件,可以通过debounce合并ajax请求事件
- 使用场景:
- debounce思路
-
是什么: 点击结束之后等delay(5s)的时间,然后触发函数,是一种结果的状态,原理就是在闭包内维护一个setTimeout定时器
- c(click) , t(trigger)
- ccccccccccc12345t—–cccccccc123c12345t—
-
lodash思路
- 检查是不是requestAnimationFrame, 这个和debounce有点类似,不过间隔是下一个page paint的时候跑,不是debounce这样指定固定时间,其实我们再debounce的时间的时候会尽可能设置为刷新屏幕的时间,比如66ms,来尽可能的提高性能.这里判断的逻辑就是
const useRAF = (!wait && wait !== 0 && typeof root.requestAnimationFrame === 'function')
- 强制转成数字或者默认的0,
wait = +wait || 0
保证wait一定是个数字,如果不是默认也是0,还挺有意思 - 强制转成true/false,比如:
leading = !!leading
- 因为lastCallTime === undefined 并且 timerId === undefined,所以先执行 leadingEdge,
- leading = true,就会执行 func。同时,这里会设置一个定时器,在等待 wait(s) 后会执行 timerExpired,timerExpired 的主要作用就是触发 trailing。
- 如果在还未到 wait 的时候就再次调用了函数的话,会更新 lastCallTime,并且因为此时 isInvoking 不满足条件,所以这次什么也不会执行。
- 时间到达 wait 时,就会执行我们一开始设定的定时器timerExpired,此时因为time-lastCallTime < wait,所以不会执行 trailingEdge。
- 这时又会新增一个定时器,下一次执行的时间是 remainingWait,如果没有 maxwait,定时器的时间是 wait - timeSinceLastCall,保证下一次 trailing 的执行,这是默认情况,如果指定了maxing,会比较出下一次 maxing 和下一次 trailing 的最小值,作为下一次函数要执行的时间。
- 检查是不是requestAnimationFrame, 这个和debounce有点类似,不过间隔是下一个page paint的时候跑,不是debounce这样指定固定时间,其实我们再debounce的时间的时候会尽可能设置为刷新屏幕的时间,比如66ms,来尽可能的提高性能.这里判断的逻辑就是
-
- underscore的思路
- 既然和时间有关,那就控制时间来确定什么时候执行,这个是underscore的思路
var later = function() { var passed = now() - previous; if (wait > passed) { timeout = setTimeout(later, wait - passed); } else { timeout = null; if (!immediate) result = func.apply(context, args); // This check is needed because `func` can recursively invoke `debounced`. if (!timeout) args = context = null; } }; var debounced = function(_args) { context = this; args = _args; previous = now(); if (!timeout) { timeout = setTimeout(later, wait); if (immediate) result = func.apply(context, args); } return result; };
- 既然和时间有关,那就控制时间来确定什么时候执行,这个是underscore的思路
- underscore的思路
-
throttle
- throttle的思路
- lodash的思路,就是直接用debounce来,指定leading, trailing就可以
function throttle(func, wait, options) { let leading = true let trailing = true if (typeof func !== 'function') { throw new TypeError('Expected a function') } if (isObject(options)) { leading = 'leading' in options ? !!options.leading : leading trailing = 'trailing' in options ? !!options.trailing : trailing } return debounce(func, wait, { leading, trailing, 'maxWait': wait }) }
- underscore的思路
export default function throttle(func, wait, options) { var timeout, context, args, result; var previous = 0; if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; var throttled = function() { var _now = now(); if (!previous && options.leading === false) previous = _now; var remaining = wait - (_now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = _now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; return throttled; }