摘要:防抖防抖的作用是将多个连续的调用合并为一次调用。防抖与节流之间的重要区别是,防抖是基于上次调用来计算的而节流是基于上次调用来计算的,只要距离上次调用超过了,就可以进行下次调用。实现修改自参考资料。个人认为实现更好理解。
防抖(debounce)
防抖的作用是将多个连续的debounced调用合并为一次func调用。作用见参考资料1。
两次debounced调用的间隔小于waitTime,则视为连续的调用。
如果距离上次debounced调用已经过去了waitTime的时间,则说明该轮连续调用已经结束(进入稳定状态)。这个时间点也被称为trailing edge。
在trailing edge以后的第一次debounced调用是下一轮连续调用的开始。当然,第一次debounced调用也是一轮连续调用的开始。这个时间点也被称为leading edge。
immediate参数可以控制是否在leading edge执行一次func调用。callAfterStable参数控制是否在trailing edge执行一次func调用。因此,func调用可以放在连续调用开始时,也可以放在结束时,也可以都放。一般设置immediate = false,callAfterStable = true,将func调用放在连续调用结束时。
假设debounced的调用一直持续不断,且相邻间隔都小于waitTime,则意味着连续调用一直没有结束,放在trailing edge的func调用一直不会执行。
function debounce( func, waitTime = 1000, immediate = false, callAfterStable = true ) { if (!immediate && !callAfterStable) throw new Error("immediate 和 callAfterStable 不能同时为false"); // 否则func.apply永远不会调用 let timeout = null; const debounced = function(...args) { // timeout的值决定当前是否处于稳定状态(已经经过waitTime没有被调用了) // 如果已经存在一个定时器,说明现在是处于一轮连续调用当中(非稳定状态),需要重新计时 if (timeout) clearTimeout(timeout); // 否则,此时是leading edge。如果配置了immediate,此时要触发func else if (immediate) func.apply(this, args); // trailing edge将在waitTime时间以后到来,进入稳定状态(前提是这段时间内没有被调用) timeout = setTimeout(() => { // 这个回调被执行时,说明已经经过waitTime没有被调用了,进入稳定状态 timeout = null; // 此时是trailing edge。如果配置了callAfterStable,要触发func if (callAfterStable) func.apply(this, args); }, waitTime); }; // 使用者可以调用这个函数,强行进入稳定状态 debounced.forceStabilize = function() { if (timeout) { clearTimeout(timeout); timeout = null; } }; return debounced; }节流(throttle)
节流的作用是限制func调用的频率(最多每waitTime调用一次)。作用见参考资料2。
防抖与节流之间的重要区别是,防抖是基于上次debounced调用来计算waitTime的;而节流是基于上次func调用来计算waitTime的,只要距离上次func调用超过了waitTime,就可以进行下次func调用。
实现2修改自参考资料2。个人认为实现1更好理解。
实现1// immediate传入true,将在leading edge就第一次调用func // 否则,将在 leading edge+waitTime 的时候才第一次调用func function throttle(func, waitTime = 1000, immediate = true) { let timeout = null, // called表示自从上次func调用以后,是否是否有调用过throttled called, // 存储上一次调用throttled时提供的args和this,用来在timeExpired时调用func lastArgs, lastThis; function timeExpired() { if (called) { func.apply(lastThis, lastArgs); called = false; timeout = setTimeout(timeExpired, waitTime); } else { // trailing edge // trailing edge不调用func了, // 因为在waitTime之前调用过了func,且自从那以后,throttled就没有被调用过。 timeout = null; // 释放内存 lastArgs = lastThis = null; } } function throttled(...args) { lastArgs = args; lastThis = this; if (!timeout) { // leading edge if (immediate) { func.apply(lastThis, lastArgs); called = false; } else { // !immediate时,leading edge下一次的timeExpired必须调用func // 否则,如果在(leading edge, leading edge + waitTime]这段时间内没有调用过throttled,func一次也不会执行 called = true; } timeout = setTimeout(timeExpired, waitTime); } else { called = true; } } throttled.cancle = function() { if (timeout) { clearTimeout(timeout); timeout = null; lastArgs = lastThis = null; } }; return throttled; }实现2
function throttle( func, waitTime = 1000, immediate = true, callAfterStable = true ) { if (!immediate && !callAfterStable) throw new Error("immediate 和 callAfterStable 不能同时为false"); // 下面会指出原因 let timeout = null, // 上一次调用func的时间 previous = 0; const throttled = function(...args) { const now = Date.now(); // immediate==false时,previous==0有特殊的含义:当前处于稳定状态,本次调用throttled不立即触发func // 阻止立即触发func的方式:previous = now,相当于0秒前刚刚调用过了func // 因此稳定状态下的第一次throttled调用会进入elseif,将func推迟调用 if (!previous && !immediate) previous = now; const remain = waitTime - (now - previous); // immediate 和 callAfterStable 不能同时为false,否则if和elseif语句块都永远不会调用 if (remain < 0 || remain > waitTime) { // 距离上一次调用func至少经过了waitTime,本次throttled立即触发func if (timeout) { // 有可能有timer回调仍阻塞在时间队列中(虽然肯定已经超时),销毁它 clearTimeout(timeout); timeout = null; } func.apply(this, args); previous = now; } else if (!timeout && callAfterStable) { // throttled调用时,距离上一次调用func还没有过去waitTime, // 不立即触发func,而是安排到previous+waitTime时刻 // 判断!timeout是为了防止安排多个func在previous+waitTime时刻调用 timeout = setTimeout(() => { func.apply(this, args); // immediate==false时,previous=0表示进入稳定状态,设置它是为了阻止下一次的immediate调用 previous = immediate ? Date.now() : 0; timeout = null; }, remain); } }; throttled.forceStabilize = function() { previous = 0; if (timeout) { clearTimeout(timeout); timeout = null; } }; return throttled; }测试代码
参考资料test
JavaScript专题之跟着underscore学防抖
JavaScript专题之跟着underscore学节流
Debouncing and Throttling Explained Through Examples
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/97482.html
摘要:若时间差大于间隔时间,则立刻执行一次函数。不同点函数防抖,在一段连续操作结束后,处理回调,利用和实现。函数防抖关注一定时间连续触发的事件只在最后执行一次,而函数节流侧重于一段时间内只执行一次。 原博客地址,欢迎star 函数防抖和节流 函数防抖和函数节流:优化高频率执行js代码的一种手段,js中的一些事件如浏览器的resize、scroll,鼠标的mousemove、mouseover...
摘要:函数防抖和节流,都是控制事件触发频率的方法。封装一个函数,让持续触发的事件监听是我们封装的这个函数,将目标函数作为回调传进去,等待一段时间过后执行目标函数第二点实现了,再看第一点持续触发不执行。 曾经面试时候被问到过这个,年少的我一脸无知。。。 后来工作中遇到了一个场景:输入名称的同时去服务器校验名称是否重复,但发现之前的代码竟然都没做限制,输入一次发一次请求。简直忍不了,就在项目的u...
阅读 2803·2021-11-24 09:39
阅读 2777·2021-09-23 11:45
阅读 3402·2019-08-30 12:49
阅读 3351·2019-08-30 11:18
阅读 1907·2019-08-29 16:42
阅读 3344·2019-08-29 16:35
阅读 1320·2019-08-29 11:21
阅读 1912·2019-08-26 13:49