函数节流和防抖
一、函数节流
所谓节流,就是控制某个函数在某一时间内,只能执行一次。应用场景一般是在拉动滚动条进行懒加载,为了防止过度请求,所以加上节流控制,防止服务器过载。
知道其原理之后,我们就可以思考一下如何进行节流。“某一时间内只能执行一次该函数”,那么我们自然而然就会想到一个函数,setTimeout()。那我们来尝试一下:
1 function throttle(func, wait) { 2 // 利用闭包,保存计时器timeout 3 let timeout; 4 return function() { 5 let context = this; // 此处的this指向的是window 6 let args = arguments; 7 // 如果计时器已经结束,那么就可以执行func函数了 8 if (!timeout) { 9 timeout = setTimeout(() => { 10 timeout = null; 11 func.apply(context, args) 12 }, wait) 13 } 14 } 15 }
但也有另外一种方法,用时间戳来控制是否执行func。
1 function throttle(func, wait) { 2 // 上一次执行的时间 3 let previous = 0; 4 return function() { 5 // 当前时间 6 let now = Date.now(); 7 let context = this; 8 let args = arguments; 9 // 若当前时间与上一次执行func的时间差超过了wait,那么就可以执行func了,并且将prev置为now 10 if (now - previous > wait) { 11 func.apply(context, args); 12 previous = now; 13 } 14 } 15 }
二、函数防抖
函数防抖与函数节流差不多,但是也有其差距。比如说,我想要让func在3秒内只能执行一次,如果使用了节流,那么在我执行完func的3秒内,无论我怎么操作都不会影响下一次执行的时间。但是如果我用了防抖,在我执行了func函数的3秒内,如果我再次执行func,那么内部的定时器将会重置,我需要再等3秒后才能执行func。
我们先看代码
1 /** 2 * @desc 函数防抖 3 * @param func 函数 4 * @param wait 延迟执行毫秒数 5 * @param immediate true 表立即执行,false 表非立即执行 6 */ 7 function debounce(func,wait,immediate) { 8 let timeout; 9 10 return function () { 11 let context = this; 12 let args = arguments; 13 14 if (timeout) clearTimeout(timeout); 15 if (immediate) { 16 var callNow = !timeout; 17 timeout = setTimeout(() => { 18 timeout = null; 19 }, wait) 20 if (callNow) func.apply(context, args) 21 } 22 else { 23 timeout = setTimeout(function(){ 24 func.apply(context, args) 25 }, wait); 26 } 27 } 28 }
我们先设置一个定时器,在需要调用函数的时候,如果发现定时器还处于计时状态,那么就将其清空(第14行),再重新进行计时。参数immediate是控制是否立刻执行func,这个可根据实际应用场景进行选择(可参考https://www.jianshu.com/p/c8b86b09daf0)。
三、总结
在进行窗口的resize、scroll,输入框内容校验等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果。