今天我们来总结一下前端JS如何实现节流、防抖,给出例子并对节流、防抖的作用作出总结。相信大家一定玩过英雄联盟或者王者荣耀等 moba 类型游戏,游戏中有技能释放和回城这两大重要功能,我们以这两个功能为例解释节流和防抖。
节流 throttle
节流其实可以理解成技能冷却中,当我们使用技能后,通常会有一定的冷却时间。对应的,当用户进行一定的请求后,我们应该对请求的频率进行限制,防止用户短时间内多次请求。
我们先用伪代码来实现这一过程:
const throttle = (fn, time) => {
let 冷却中 = false
return (...args) => {
if(冷却中) return
fn.call(undefined, ...args)
冷却中 = true
setTimeout(()=>{
冷却中 = false
}, time)
}
}
以上的例子就是一个可以给任何函数使用的节流函数。其中,我们通过输入参数 fn 和 time 来控制目标函数和节流时间,通过冷却中来实现计时器的功能。
- 先让
冷却中 = flase,即表示技能没有在冷却,可以释放 - 再控制节流函数返回一个函数,返回的这个函数会接受节流函数传入的所有参数
- 加上一个判断,如果
冷却中 = true,表示技能正在冷却,则请求中断,直接 return - 否则,就执行目标函数,因为使用的是箭头函数,所以 this 为 undefined
- 将
冷却中置为 true - 最后接一个 setTimeout 函数,控制在 time 后才将计时器置为 true ,则在此期间,技能都是在冷却中,再次请求施放技能将被中断
将上述函数写成正确的形式就是节流的普通形式:
const throttle = (f, time) => {
let timer = null
return (...args) => {
if(timer) {return}
f.call(undefined, ...args)
timer = setTimeout(()=>{
timer = null
}, time)
}
}
防抖 debounce
理解了节流,防抖就更好理解了。防抖可以理解成回城功能,在回城期间是不能再次操作的,否则回城将被打断,计时器会重新计时。将上面过程写成伪代码如下:
const debounce = (fn, time) => {
let 回城计时器 = null
return (...args)=>{
if(回城计时器 !== null) {
clearTimeout(回城计时器) // 打断回城
}
// 重新回城
回城计时器 = setTimeout(()=>{
fn.call(undefined, ...args) // 回城后调用 fn
回城计时器 = null
}, time)
}
}
- 类似节流,防抖函数也接受一个 fn 和 time
- 首先将
回城计时器置为空 - 防抖函数返回一个函数,这个函数也接受防抖函数给的所有参数
- 如果此时
回城计时器不为空,就表示正在回城,那么回城将被打断,然后进入后面重新回城 - 让
回城计时器等于一个 setTimeout 函数,当一段时间 time 过后的回城成功后调用一开始传入的 fn,然后将回城计时器置为空
写成普通的防抖函数形式为:
const debounce = (fn, time) => {
let timer = null
return (...args)=>{
if(timer !== null) {
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.call(undefined, ...args)
timer = null
}, time)
}
}
总结
最后是对节流和防抖使用场景的总结:
- 节流的使用能够避免用户频繁点击按钮,因为它会在计时的过程中直接中断再次的请求
- 防抖的使用能够避免用户频繁的拖动操作带来的多次更新页面,它会在用户最后一次操作结束后进行重新计时,计时器结束后才会执行