_.throttle(func, [wait=0], [options={}])
114
_.throttle(func, [wait=0], [options={}])
创建一个节流函数,每调用一次func函数的时候至少要等待`wait`毫秒(或者每一次调用等待浏览器重绘页面帧)。节流函数有一个cancel方法来取消延迟的func调用,还有一个flush方法来立即调用func函数。也提供了option参数来表明func函数是否应该在等待wait时间开始之前调用还是wait时间过后调用。func函数调用会带着提供给节流函数的最后一个配置参数。之后对于节流函数的调用返回最后一次func调用的结果。
注意:如果leading和trailing配置项是true,func只有在节流函数等待wait时间度过的时候被调用超过一次时才会在wait时延过后再次调用。
如果wait等待时间是0,并且leading是false,func的调用会推迟至下一次事件循环,与setTimeout设置延迟时间为0一样
如果wait参数在使用requestAnimationFrame的环境中被省略,func会延迟至下一次页面重绘的时候被调用,通常延迟16ms
//使用场景和例子 //当滚动页面的时候避免过度更新滚动位置 /* jQuery(window).on('scroll', throttle(updatePosition, 100)) */ //当点击事件被触发的时候调用renewToken,但是每五分钟才能调用一次 /* const throttled = throttle(renewToken, 300000, { 'trailing': false }) jQuery(element).on('click', throttled) */ //当页面路由变化的时候,取消等待时间过后结尾的节流函数调用 //jQuery(window).on('popstate', throttled.cancel)
参数
func (Function): 需要节流的函数
[wait=0] (number): 需要节流调用等待的时间
[options={}] (Object): 配置对象
[options.leading=true] (boolean): 在func被调用之前的最大延迟时间
[options.trailing=true] (boolean): 是否在wait时延过后调用
返回值
(Function): 返回新的节流函数
例子
// Avoid excessively updating the position while scrolling. jQuery(window).on('scroll', _.throttle(updatePosition, 100)); // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); jQuery(element).on('click', throttled); // Cancel the trailing throttled invocation. jQuery(window).on('popstate', throttled.cancel);
节流与去抖的区别
throttle节流函数与debounce去抖函数之间的区别
节流函数默认leading和trailing都是true,所以在等待时间内,如果触发超过一次,那么wait时延过后就会主动触发
去抖函数默认leading为false,trailing为true,如果一直不停的触发,那么会不停地重置定时器,所以直到停止不停触发事件结束后等待最后一个wait时间过后才会触发事件处理程序
源代码
import debounce from './debounce.js' import isObject from './isObject.js' /** * Creates a throttled function that only invokes `func` at most once per * every `wait` milliseconds (or once per browser frame). The throttled function * comes with a `cancel` method to cancel delayed `func` invocations and a * `flush` method to immediately invoke them. Provide `options` to indicate * whether `func` should be invoked on the leading and/or trailing edge of the * `wait` timeout. The `func` is invoked with the last arguments provided to the * throttled function. Subsequent calls to the throttled function return the * result of the last `func` invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is * invoked on the trailing edge of the timeout only if the throttled function * is invoked more than once during the `wait` timeout. * * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred * until the next tick, similar to `setTimeout` with a timeout of `0`. * * If `wait` is omitted in an environment with `requestAnimationFrame`, `func` * invocation will be deferred until the next frame is drawn (typically about * 16ms). * * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) * for details over the differences between `throttle` and `debounce`. * * @since 0.1.0 * @category Function * @param {Function} func The function to throttle. * @param {number} [wait=0] * The number of milliseconds to throttle invocations to; if omitted, * `requestAnimationFrame` is used (if available). * @param {Object} [options={}] The options object. * @param {boolean} [options.leading=true] * Specify invoking on the leading edge of the timeout. * @param {boolean} [options.trailing=true] * Specify invoking on the trailing edge of the timeout. * @returns {Function} Returns the new throttled function. * @example * * // Avoid excessively updating the position while scrolling. * jQuery(window).on('scroll', throttle(updatePosition, 100)) * * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. * const throttled = throttle(renewToken, 300000, { 'trailing': false }) * jQuery(element).on('click', throttled) * * // Cancel the trailing throttled invocation. * jQuery(window).on('popstate', throttled.cancel) */ //创建一个节流函数,每调用一次func函数的时候至少要等待`wait`毫秒(或者每一次调用等待浏览器重绘页面帧)。节流函数有一个cancel方法来取消延迟的func调用,还有一个flush方法来立即调用func函数。也提供了option参数来表明func函数是否应该在等待wait时间开始之前调用还是wait时间过后调用。func函数调用会带着提供给节流函数的最后一个配置参数。之后对于节流函数的调用返回最后一次func调用的结果。 //注意:如果leading和trailing配置项是true,func只有在节流函数等待wait时间度过的时候被调用超过一次时才会在wait时延过后再次调用。 //如果wait等待时间是0,并且leading是false,func的调用会推迟至下一次事件循环,与setTimeout设置延迟时间为0一样 //如果wait参数在使用requestAnimationFrame的环境中被省略,func会延迟至下一次页面重绘的时候被调用,通常延迟16ms //使用场景和例子 //当滚动页面的时候避免过度更新滚动位置 /* jQuery(window).on('scroll', throttle(updatePosition, 100)) */ //当点击事件被触发的时候调用renewToken,但是每五分钟才能调用一次 /* const throttled = throttle(renewToken, 300000, { 'trailing': false }) jQuery(element).on('click', throttled) */ //当页面路由变化的时候,取消等待时间过后结尾的节流函数调用 //jQuery(window).on('popstate', throttled.cancel) function throttle(func, wait, options) { let leading = true let trailing = true if (typeof func != 'function') {//如果func不是function,抛出错误 throw new TypeError('Expected a function') } if (isObject(options)) {//提取options中的leading和trailing参数,默认都是true leading = 'leading' in options ? !!options.leading : leading trailing = 'trailing' in options ? !!options.trailing : trailing } return debounce(func, wait, {//调用去抖函数处理 'leading': leading, 'maxWait': wait, 'trailing': trailing }) } export default throttle
debounce
import isObject from './isObject.js' import root from './.internal/root.js' /** * Creates a debounced function that delays invoking `func` until after `wait` * milliseconds have elapsed since the last time the debounced function was * invoked, or until the next browser frame is drawn. The debounced function * comes with a `cancel` method to cancel delayed `func` invocations and a * `flush` method to immediately invoke them. Provide `options` to indicate * whether `func` should be invoked on the leading and/or trailing edge of the * `wait` timeout. The `func` is invoked with the last arguments provided to the * debounced function. Subsequent calls to the debounced function return the * result of the last `func` invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is * invoked on the trailing edge of the timeout only if the debounced function * is invoked more than once during the `wait` timeout. * * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred * until the next tick, similar to `setTimeout` with a timeout of `0`. * * If `wait` is omitted in an environment with `requestAnimationFrame`, `func` * invocation will be deferred until the next frame is drawn (typically about * 16ms). * * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) * for details over the differences between `debounce` and `throttle`. * * @since 0.1.0 * @category Function * @param {Function} func The function to debounce. * @param {number} [wait=0] * The number of milliseconds to delay; if omitted, `requestAnimationFrame` is * used (if available). * @param {Object} [options={}] The options object. * @param {boolean} [options.leading=false] * Specify invoking on the leading edge of the timeout. * @param {number} [options.maxWait] * The maximum time `func` is allowed to be delayed before it's invoked. * @param {boolean} [options.trailing=true] * Specify invoking on the trailing edge of the timeout. * @returns {Function} Returns the new debounced function. * @example * * // Avoid costly calculations while the window size is in flux. * jQuery(window).on('resize', debounce(calculateLayout, 150)) * * // Invoke `sendMail` when clicked, debouncing subsequent calls. * jQuery(element).on('click', debounce(sendMail, 300, { * 'leading': true, * 'trailing': false * })) * * // Ensure `batchLog` is invoked once after 1 second of debounced calls. * const debounced = debounce(batchLog, 250, { 'maxWait': 1000 }) * const source = new EventSource('/stream') * jQuery(source).on('message', debounced) * * // Cancel the trailing debounced invocation. * jQuery(window).on('popstate', debounced.cancel) * * // Check for pending invocations. * const status = debounced.pending() ? "Pending..." : "Ready" */ //创建一个去抖函数来推迟调用func,自从上一次去抖函数被调用之后等待wait毫秒时间过后再调用,或者等待直到下一次浏览器帧被重新绘制。创建去抖函数的同时也会创建一个cancel方法去取消延迟func调用,还有一个flush方法来立即调用。也提供了option参数来表明func函数是否应该在等待wait时间开始之前调用还是wait时间过后调用。func函数调用会带着提供给去抖函数的最后一个配置参数。之后对于去抖函数的调用返回最后一次func调用的结果。 //注意:如果leading和trailing配置项是true,func只有在去抖函数等待wait时间度过的时候被调用超过一次时才会在wait时延过后再次调用。 //如果wait等待时间是0,并且leading是false,func的调用会推迟至下一次事件循环,与setTimeout设置延迟时间为0一样 //如果wait参数在使用requestAnimationFrame的环境中被省略,func会延迟至下一次页面重绘的时候被调用,通常延迟16ms //使用场景 //当window大小不断变化的时候避免昂贵的计算,例如jQuery(window).on('resize', debounce(calculateLayout, 150)) //当点击的时候调用`sendMail`,将随后的调用去抖 // jQuery(element).on('click', debounce(sendMail, 300, { // 'leading': true, // 'trailing': false // })) //确保`batchLog`在去抖化调用后1秒钟被调用1次 // const debounced = debounce(batchLog, 250, { 'maxWait': 1000 }) // const source = new EventSource('/stream') // jQuery(source).on('message', debounced) //取消wait时延过后的去抖调用 //jQuery(window).on('popstate', debounced.cancel) //检查是否调用处于等待状态 //const status = debounced.pending() ? "Pending..." : "Ready" //func是需要去抖的方法 //wait推迟执行的毫秒数,如果省略这个参数,requestAnimationFrame会被使用 //requestAnimationFrame浏览器在下一次重绘之前调用指定的函数 //options中的配置项 //options.leading在wait时延之前调用 //options.trailing在wait时延过后调用 //options.maxWait在func被调用之前的最大延迟时间 function debounce(func, wait, options) { let lastArgs, lastThis, maxWait,//func调用之前的最大延迟时间 result, timerId,//定时器 lastCallTime let lastInvokeTime = 0//上一次调用func的时间 let leading = false//是否在wait时延之前调用 let maxing = false//是否设置了func调用之前的最大延迟时间 let trailing = true//是否在wait时延过后调用 // Bypass `requestAnimationFrame` by explicitly setting `wait=0`. //忽略requestAnimationFrame通过明确地设置wait=0 const useRAF = (!wait && wait !== 0 && typeof root.requestAnimationFrame === 'function') if (typeof func != 'function') {//如果func不是function类型,抛出错误 throw new TypeError('Expected a function') } wait = +wait || 0//将wait转换成数字 if (isObject(options)) {//如果options是对象 leading = !!options.leading//是否在wait时延之前调用 maxing = 'maxWait' in options//是否设置了func调用之前的最大延迟时间 maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait//func调用之前的最大延迟时间 trailing = 'trailing' in options ? !!options.trailing : trailing//是否在wait时延过后调用 } function invokeFunc(time) {//执行func const args = lastArgs const thisArg = lastThis lastArgs = lastThis = undefined//debounced接收参数和this置空 lastInvokeTime = time//更新上一次执行func时间 result = func.apply(thisArg, args)//调用func获取结果返回 return result } function startTimer(pendingFunc, wait) {//启动定时器 if (useRAF) { return root.requestAnimationFrame(pendingFunc) } return setTimeout(pendingFunc, wait) } function cancelTimer(id) {//取消定时器 if (useRAF) { return root.cancelAnimationFrame(id) } clearTimeout(id) } function leadingEdge(time) {//leading时调用 // Reset any `maxWait` timer. lastInvokeTime = time//重置上一次func执行时间 // Start the timer for the trailing edge. timerId = startTimer(timerExpired, wait)//为wait时延过后的trailing调用开启定时器 // Invoke the leading edge. return leading ? invokeFunc(time) : result//如果leading为true,wait时延之前调用,直接调用,否则返回result,undefined } function remainingWait(time) {//还剩下多少时间可以执行下一次 const timeSinceLastCall = time - lastCallTime//距离上次去抖时间 const timeSinceLastInvoke = time - lastInvokeTime//距离上次func执行时间 const timeWaiting = wait - timeSinceLastCall//距离上次去抖时间还需要等多久,下一次trailing时间 return maxing ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting //如果有传递最大等待时间,就在下一次trailing时间和maxWait - timeSinceLastInvoke里找最小的 //如果没有传递最大等待时间,返回下一次trailing时间 } function shouldInvoke(time) {//判断在time时间点是否func应该被调用 const timeSinceLastCall = time - lastCallTime//距离上次去抖时间 const timeSinceLastInvoke = time - lastInvokeTime//距离上次func执行时间 // Either this is the first call, activity has stopped and we're at the // trailing edge, the system time has gone backwards and we're treating // it as the trailing edge, or we've hit the `maxWait` limit. return (lastCallTime === undefined || (timeSinceLastCall >= wait) || (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)) //lastCallTime === undefined首次调用 //timeSinceLastCall >= wait距离上次被去抖已经超过 wait //timeSinceLastCall < 0//系统时间倒退 //maxing && timeSinceLastInvoke >= maxWait//距离上次func执行时间已经超过最大等待时间 } function timerExpired() {//等待wait时间后触发trailing const time = Date.now()//当前时间 if (shouldInvoke(time)) {//如果当前时间应该调用,就调用trailingEdge判断是否已经去抖过一次 return trailingEdge(time) } // Restart the timer. timerId = startTimer(timerExpired, remainingWait(time))//如果此刻不能执行func,就重新启动定时器,时间为剩余等待时间 } function trailingEdge(time) {//trailing时调用func timerId = undefined//定时器置空 // Only invoke if we have `lastArgs` which means `func` has been // debounced at least once. if (trailing && lastArgs) {//只有在拥有lastArgs参数的时候才执行func,lastArgs说明func被去抖至少一次 return invokeFunc(time) } lastArgs = lastThis = undefined//lastArgs和lastThis置空 return result//返回结果undefined } function cancel() {//取消定时器,并将上一次调用时间清0,上一次debounced接收参数,this,上一次去抖时间都置空 if (timerId !== undefined) { cancelTimer(timerId) } lastInvokeTime = 0 lastArgs = lastCallTime = lastThis = timerId = undefined } function flush() {//如果没有定时器,返回当前结果,如果有定时器,调用trailingEdge return timerId === undefined ? result : trailingEdge(Date.now()) } function pending() {//查看当前是否处于等待状态 return timerId !== undefined } function debounced(...args) { const time = Date.now()//当前时间毫秒数 const isInvoking = shouldInvoke(time)//判断当前时间是否func应该被调用 lastArgs = args//参数数组 lastThis = this//本次this lastCallTime = time//去抖时间 if (isInvoking) {//如果此刻需要被调用,1说明是首次调用且是leading调用 2距离上次去抖wait时间了 3距离上次执行时间过去了最大等待时间 if (timerId === undefined) {//如果没有定时器说明1首次调用2刚执行过取消操作或者trailing调用 return leadingEdge(lastCallTime)//调用leadingEdge判断是否leading时需要调用 } if (maxing) {//如果不是第一次调用且设置了最大延迟时间,说明已经超过了最大延迟时间,直接调用返回结果 // Handle invocations in a tight loop. timerId = startTimer(timerExpired, wait)//开启一个定时器,等待时间wait,判断是否wait时间后需要出发trailing调用 return invokeFunc(lastCallTime)//调用func返回结果 } } if (timerId === undefined) {//如果此刻不需要被调用,且定时器没有开启,就开启一个定时器,等待时间wait timerId = startTimer(timerExpired, wait) } return result } debounced.cancel = cancel debounced.flush = flush debounced.pending = pending return debounced } export default debounce

浙公网安备 33010602011771号