We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
这两者是有明显区别的. debounce 即是防止跳动. throttle则是掐死. 这2者的目的, 都是为了减少请求或者触发事件的次数.可以总结为, throttle 是减少频率 debounce 是汇总, 最后一次发送
// 简单debounce function debounce(fn, wait = 300){ let timer, fnWrapper; function clear(){ clearTimeout(timer); } fnWrapper = function (){ if(timer) clear(); timer = setTimeout(function(){ fn(); }, wait); } return fnWrapper; }
如果在wait时间内有debounce过来, 则关闭定时器, 重启一个定时器, wait时间之后,执行fn
// 简单throttle function throttle(fn, wait){ let wrapperFn, lastTime = 0; wrapperFn = function(){ if(Date.now() - lastTime < wait) return; lastTime = Date.now(); return fn(); } return wrapperFn; }
距离上一次调用的时间间隔要大于wait
以上是基本理念, 和实际实现有区别
// lodash 源码实现的解读 function debounce(func, wait, options) { var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } wait = toNumber(wait) || 0; if (isObject(options)) { leading = !!options.leading; maxing = 'maxWait' in options; maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; trailing = 'trailing' in options ? !!options.trailing : trailing; } /** * 执行函数 * 执行完毕清空配置信息 * 但保留和更新lastInvokeTime */ function invokeFunc(time) { var args = lastArgs, thisArg = lastThis; lastArgs = lastThis = undefined; lastInvokeTime = time; result = func.apply(thisArg, args); return result; } /** * 第一次调用 * 同时开启一个setTimeout, 在wait之后调用timerExpired * 如果在wait之间存在debounce的调用, 那么lastArgs, thisArg 就会记录最近的一次调用的信息 * 在本次wait等待时间完毕了之后, 用lastArgs, thisArg等参数调用最近一次的func, 完毕之后,清空调用信息, * 和cancel不同的是, 保留lastInvokeTime时间 */ function leadingEdge(time) { lastInvokeTime = time; // 在wait时间之后再执行一次timerExpired , 检查是否应该执行trailinFn, 防抖 timerId = setTimeout(timerExpired, wait); return leading ? invokeFunc(time) : result; } /** * 过期剩余时间 * 如果有maxing, 证明这是最大等待时间 * 那么返回的就是 距离上次调用debounce和距离最大等待时间之间的最小值 */ function remainingWait(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, timeWaiting = wait - timeSinceLastCall; return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; } /** * 这是判断是否到执行函数的时间 * 1. 第一次调用 * 2. 距离上次调用debounce的时间 > wait 等待时间 * 3. 系统时间回调, 导致timeSinceLastCall<0的情况 * 4. 等待时间超过最大时长 */ function shouldInvoke(time) { var timeSinceLastCall = time - lastCallTime, /* 调用debounce的时间 */ timeSinceLastInvoke = time - lastInvokeTime; /* 调用func的时间 */ return (lastCallTime === undefined || (timeSinceLastCall >= wait) || (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); } /** * 判断下一轮执行func的时间, 用递归调用setTimeout的方式来实现 * 如果没有到shouldInvoke=true的时候, 重新计算setTimeout的剩余等待时间 * 到了showInvoke的时间, 则执行防抖的延迟执行 */ function timerExpired() { var time = now(); if (shouldInvoke(time)) { return trailingEdge(time); } // 如果还没到下一轮func的调用的时间, // 那么重新开始定时器, 时间是距离maxWait和距离下一次执行func的最小值 timerId = setTimeout(timerExpired, remainingWait(time)); } // 这个是防抖的延迟执行, 也就是需要经过wait, 或者最大的等待时间 maxWait的时间之后,才会触发 function trailingEdge(time) { timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been // debounced at least once. // 用lastArgs来记住最近的一次debounce的时间和时间参数, 在wait时间之后再进行一次防抖. if (trailing && lastArgs) { return invokeFunc(time); } lastArgs = lastThis = undefined; return result; } // 手动取消, 会重置所有的配置, cancel之后, 再起调用debounce相等于首次调用debounce, 会触发leadingEdge function cancel() { if (timerId !== undefined) { clearTimeout(timerId); } lastInvokeTime = 0; lastArgs = lastCallTime = lastThis = timerId = undefined; } // 手动调用 function flush() { return timerId === undefined ? result : trailingEdge(now()); } function debounced() { var time = now(), isInvoking = shouldInvoke(time); // 判断可以被调用 lastArgs = arguments; // 调用的参数 lastThis = this; // 调用的this lastCallTime = time; // 调用debounce的时间, 需要区分lastInvokeTime, 这个fn的执行时间 if (isInvoking) { /** * 第一次调用, 或者手动flush之后的一次调用, 或者在执行了trailinEdge 成功invokeFunc之后的调用 */ if (timerId === undefined) { return leadingEdge(lastCallTime); } /** * shouldInvoke=true, 但是存在timerId, 比如 系统时间回调的情况下, 或者maxWait比wait时间短的情况下,之间调用invokeFunc, 同时更新timerId */ if (maxing) { // Handle invocations in a tight loop. timerId = setTimeout(timerExpired, wait); return invokeFunc(lastCallTime); } } /** * 这一步是为了处理, 在手动flush的情况下, timerId会被重置, 但是又没有到shouldInvoke当前情况下, * 重新开始一次防抖时间轮询, 期间如果有debounce被调用, 但是还是shouldInvoke=false, 那么timerExpired * 的trailinEdge就有可能被调用. */ if (timerId === undefined) { timerId = setTimeout(timerExpired, wait); } return result; } debounced.cancel = cancel; debounced.flush = flush; return debounced; }
注: 本人的github地址为: lodash源码阅读(3)-的debounce, throttle, 如有版权问题或其他问题, 请求给作者留言, 感谢!
The text was updated successfully, but these errors were encountered:
No branches or pull requests
防抖和节流的区别
这两者是有明显区别的.
debounce 即是防止跳动.
throttle则是掐死.
这2者的目的, 都是为了减少请求或者触发事件的次数.可以总结为, throttle 是减少频率
debounce 是汇总, 最后一次发送
简单实现
如果在wait时间内有debounce过来, 则关闭定时器, 重启一个定时器, wait时间之后,执行fn
距离上一次调用的时间间隔要大于wait
lodash 的实现
小结主要流程
流程图
The text was updated successfully, but these errors were encountered: