防抖和节流
# 前言
一个老生长谈的话题,简单实现一下,加深印象
# 为什么需要防抖和节流
频繁的触发某些事件对于浏览器来说是一个不小的负担,并且对于某些业务逻辑来说也会造成意外的bug。所以在日常开发中就需要用到防抖和节流这两个手段来优化我们的代码,使之更健壮
# 防抖
理解防抖
通过英雄联盟回城的一个例子来具象化一下防抖,通常情况下我们按下回城需要经过一个8秒的引导,在此期间可以被任意东西打断,比如自己移动,释放技能,被敌方攻击等。一旦被打断就需要重新引导8秒。防抖就是这样,我们将需要触发的函数当做是回城,而防抖会有一个时间间隔,这个时间间隔就是回城所需要的那8秒,当我们触发防抖后并不会立即执行回调函数,而是会等待一下这个引导时间。在此期间内如果再次触发了就取消上一次的回城,接着重新开始计时。只有当计时期间内没有被打断,才能正确的去执行回调函数。
下面来通过代码实现一下
/**
* 防抖函数
* @param cb 需要被执行的回调
* @param delay 回调执行的延迟时间
*/
const debounce = (cb, delay) => {
let timer = null // 此处声明一个变量用于保存状态,下文会通过闭包引用这个状态
return function (...args) {
if (timer) { // 首先判断变量是否已被赋值,如果有这说明上一次的引导还没结束,所以需要取消掉上次的引导
clearTimeout(timer)
}
timer = setTimeout(() => { // 如果变量没值,则开始进行引导。通过定时器来模拟引导时间,然后执行回调
cb(...args)
clearTimeout(timer)
}, delay)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
debounce工具会返回一个新的函数,这个新的函数里通过闭包去访问debounce函数提供的状态变量。然后根据这个状态变量是否有值来选择是否清楚之前的定时器,然后重新执行新的定时器。
# 使用场景
防止用户重复点击,快速点击,误操作的场景都可以使用。例如表单的重复提交,订单的重复提交。等一系列对数据单一性要求较高的场景。还有一些性能优化的场景,例如搜索框的联想搜索等
# 节流
理解节流
上文中我们把防抖比作了英雄联盟的回城,那么节流则可以比作是释放技能。众所周知,释放技能是会有一个技能cd的。一旦技能进入cd我们再按技能就无效了,必须等cd转完才可以释放下一次技能。节流同样是起到这么一个效果。触发回调会有一个cd时间。当这个cd过去后才可以再次触发回调
下面同样通过代码来实现一下节流函数
/**
* 节流函数
* @param cb 需要被执行的回调
* @param delay 间隔时间
*/
const throttle = (cb,delay)=>{
let timer = null
return function (...args){
if(timer) return // 如果定时器存在,则直接返回,表示当前处于cd中
cb(...args)
timer = setTimeout(()=>{ // 在cd结束后清空定时器,标识cd已经转好,可以再次执行
timer = null
},delay)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 使用场景
虚拟列表的性能优化,以及一些需要间隔一段时间才能重复触发的场景
# 总结
防抖和节流非常的相似,唯一的不同电视防抖保证一定的时间内只触发一次回调函数,如果在此期间多次调用的话将会延后回调函数的触发时间(因为每次被调用都会重新开始计时),但最终都只会执行一次,直到整个防抖周期结束,也就是回调函数被执行之后。 节流则稍有不同,他的宗旨是同样的事情每隔一段时间才可以被重复触发,如果在间隔时间内重复调用,则只有第一次调用生效,后续的调用都将无效。必须等待间隔时间过去之后才可以再次生效。