防抖与节流
前言
防抖和节流是我们前端开发过程中常用的两种函数优化手段,一般是用来限制函数的执行次数,提升性能和用户体验。防抖节流也是曾经被大厂面试过程中问到过的问题,如果你还不太清楚是什么东西,那就赶紧来学习一下吧。
防抖
防抖的概念
当某个函数持续,频繁的触发,那么只在它最后一个函数,且一段时间内没有再次触发,这个函数才会执行,拿一个按钮的点击事件来说,当你多次点击这个按钮并且触发事件对应的回调函数,这个函数将不会执行,只有当你最后一次点击之后,且规定的时间之内没有再次去点击这个按钮,这个回调才会触发!
就好比你在按电梯的按钮。你按了一次,电梯并不会立刻启动,而是等待一段时间,如果在这段时间内有人再按了一次,那么等待的时间就会被重置。只有当一段时间内没有新的按压事件发生,电梯才会启动。
防抖的应用场景
一般防抖的应用场景有:
- 输入框搜索:当用户在搜索框中输入关键字时,使用防抖可以避免频繁发送搜索请求,而是在用户停止输入一段时间后才发送请求,减轻服务器压力。
- 按钮点击:当用户点击按钮时,使用防抖可以避免用户多次提交或重复操作。
为什么要防抖
在我们的前端项目开发中,假如我们要给后端发送请求,万一手抖多点了几次,多发送了几遍怎么办,这时候就需要我们的防抖函数来处理了。给大家假设一个场景:当我们在使用我们的页面,我们页面有一个提交数据的按钮!我们每次点击提交按钮,都会向我们的后端服务器发送一个请求!接下来我们来构建一下这样一个简单的页面
xml
<button id="submit">提交</button>
<script>
let sub = document.getElementById('submit')
sub.addEventListener('click',function(){
console.log('已提交');
})
</script>
大家想象一下,咱们每次点击按钮都会输出一个"已提交",代表咱们向后端服务器发送了一个请求!虽然咱们的请求发送成功了,但是大家有没有发现一个问题,就是如果我们多次点击,就会多次输出"已提交",也就意味着我们点了多次,就会向我们的服务器后端发送多个请求!!
这样的话就会导致许多不好的后果像:
1.服务器的压力增大,在同一时间段内一个人同时发送多个请求给我们的后端,每点击一次就会向服务器发送一个请求,如果点击的频率过高或者同时点击的人数非常多,此时服务器可能就会承受不住这么大的压力而导致服务器崩溃!
2.数据重复:同一时间发送多个请求给我们的后端,还可能会导致后端处理重复的数据,这不是妥妥的白做嘛
3.资源浪费:过多的请求会消耗更多的网络资源,可能会导致网络拥堵,影响其他网络活动,甚至导致网络瘫痪!
4.安全性问题:如果每次点击都会发送敏感信息像个人信息和密码等等,那么可能就会增加这些信息被截获的风险。
如何实现防抖
现在我们就以点击按钮为例,咱就是希望点击提交按钮,控制台能输出一个提交完成,并且规定时间为1s,也就是说这里的防抖就是我在两次点击如果都在1s内是不会打印内容的,只有间隔时间超过1s,我们才能打印,如何手写一下呢?在js中,防抖通常通过设置一个延迟时间来实现。当事件触发时,我们会等待一段时间,如果在这段时间内没有再次触发事件,那么我们执行相应的操作。
来看代码:
xml
<body>
<button id="btn">提交</button>
<script>
function send(e){
console.log(this,'已提交',e);
}
let btn = document.getElementById("btn");
btn.addEventListener("click", debounce(send,1000))
function debounce(fn, delay){
let timer
return function(){
let args = arguments
if(timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.call(this,...args)
},delay)
}
}
</script>
</body>
1.btn.addEventListener("click", debounce(send,1000))
事件监听器让括号中的debounce函数this指向了btn,而send函数的this又被call指向了debounce,因此send中this指向了btn。
2.debounce防抖函数中返回一个函数,这个函数会用到timer这个变量,因此形成一个闭包,使得timer变量在多次调用中保持一致。
3.如果timer存在,那么clearTimeout()
就掐灭定时器,如果不存在就给它赋值一个定时器,1s后执行send函数,因为send词法环境在全局中,所以要用call改变其指向
4.每当你点击一次按钮,都会触发一次debounce函数,第一次点击timer不存在,因此获得一个定时器,1s后执行send函数,第二次点击如果在1s内,那么timer存在,就会掐灭定时器,就这样,执行定时器永远都是在最后一次点击1s后。
防抖的好处
- 减少不必要的计算和渲染:对于一些高开销的操作,如网络请求、复杂的计算或者DOM操作,防抖可以确保这些操作只在用户停止操作一段时间后执行一次,从而避免了因连续快速的操作导致的重复计算和界面频繁刷新,提高了性能和用户体验。
- 提高效率和响应速度:通过减少不必要的函数执行,可以有效减轻CPU和内存的负担,尤其是在移动设备或性能较低的设备上,这一点尤为重要。同时,因为减少了请求或计算的数量,使得真正需要处理的请求能够更快得到响应。
- 节省网络资源:对于需要发起网络请求的场景,如搜索建议、表单验证等,防抖可以避免因用户快速输入而导致的多次无谓请求,从而节约了网络带宽和服务器资源。
- 提升用户体验:频繁的操作和响应可能会让用户感到界面"卡顿"或难以控制,防抖确保了只有在用户完成操作(如停止输入、停止滚动等)后才执行相应的动作,使得界面反应更加自然流畅。
节流
节流的概念
节流技术确保函数在给定的时间间隔内最多只执行一次。不论事件触发多少次,都会按照设定的时间间隔执行。这适用于那些需要限制函数执行频率但又不能完全忽略中间操作的场景,比如滚动事件处理、鼠标移动跟踪等。
在一段时间内,某个函数持续,频繁的触发,只会执行第一次函数的触发,忽略掉后面函数的执行。
节流的应用场景
- 滚动事件监听,用于懒加载图片或滚动到顶部/底部的提示。
- 鼠标移动或触摸屏滑动事件的处理,用于平滑动画效果。
- 实时表单验证,限制验证频率以提升性能。
为什么要节流
当用户向我们的服务器发送请求时,都希望立马得到后端服务器的响应,可是,后端服务器一次也只能对一些人或者一些请求服务呀,举个例子,你是餐厅里的唯一一个服务员,这个餐厅非常火爆,各种订单络绎不绝,所有的订单全部一股脑的塞给你,可是你只有一个人呀,一次只能为一个顾客服务,并不能同时为所有人服务。于是就会有人说了,可以让他们排队呀!没错!这就是为什么,咱们要对前端用户的请求进行限流,咱们需要设定规则,让顾客排队一个一个来,咱们的后端服务器也是一样的,比如每次我们只能处理五六个请求,而其他请求需要排队等候,这就是节流,控制了处理请求的速度,防止因为太多的请求而导致我们的服务器"崩溃"。
与此同时,我们也可以设置规则,比如在一秒钟之内,咱们只处理一个请求,或者限制每个用户每分钟只能发送一次请求,或者规定一段时间内处理的订单总数,这样我们也可以更加有序的处理请求,确保每个人都能尽快的享受到请求服务。
如何实现节流
今天咱们就指定一个规则,这个规则就是我们的服务器在2s内只会处理一条请求,怎么实现呢?来看代码:
xml
<button id="btn">提交</button>
<script>
let btn = document.getElementById('btn')
function send(){
console.log('提交了');
}
btn.addEventListener('click',throttle(send,2000))
function throttle(fn,delay){
let prevTime = Date.now()
return function(){
if(Date.now()-prevTime>delay){
fn.apply(this,arguments)
prevTime =Date.now()
}
}
}
</script>
- 首先,通过
document.getElementById('btn')
获取id为"btn"的按钮元素,并将其赋值给变量btn
。 - 然后,定义了一个名为
send
的函数,该函数简单地在控制台打印出"提交了",模拟了一个可能需要节流处理的事件回调。 - 接下来,使用
btn.addEventListener('click', throttle(send, 2000))
为按钮的点击事件添加了一个监听器,其中throttle(send, 2000)
表示将send
函数通过节流函数throttle
处理后作为监听器,2000毫秒是设置的延迟时间。 throttle
函数接收两个参数:要节流的函数fn
和延迟时间delay
。它返回一个新的函数,这个新函数会在每次调用时检查当前时间与上一次调用时间的差是否大于设定的延迟。如果是,则执行原函数fn
并更新上一次调用时间prevTime
;否则,不执行函数,直接返回,从而达到节流的效果。
通过这种方式,即使用户快速连续点击按钮,send
函数也不会被过度执行,而是至少每隔2秒才执行一次,有效控制了函数执行的频率。
节流的好处
- 控制函数执行频率,避免过度消耗资源。
- 保持操作的连续性反馈,比如在滚动事件中保持平滑的滚动体验。
- 在不影响用户体验的前提下减少计算负担。
- 提升系统的稳定性
总结
防抖和节流都是用来优化高频率事件处理的策略,但防抖更倾向于在操作结束时才执行,防抖是一种延迟策略,即一段连续的触发事件结束后,等待一段时间再去执行;而节流则确保在操作过程中有规律地执行。节流是一种控制函数执行频率的策略,一段时间内只执行一次。选择哪种策略取决于具体需求:如果咱们关心的是最后一次操作的结果,应该使用防抖;如果希望在操作过程中持续有响应但限制频率,则应使用节流。