防抖与节流
4/22/2024 随便
# 防抖
当持续触发事件时,一定时间段内没有再触发事件,函数才会执行一次,如果在这个时间段内又触发了事件,则会重新开始延时。常用于输入框搜索、滚动加载等场景。
function debounce(fn, delay = 300) {
let timer = null;
return function(...args) {
clearTimeout(timer)
timer = setTimeout(()=>{
fn.call(this, ...args)
}, delay)
}
}
# 节流
在一定时间范围内,用户触发多次只会执行一次以达到防止用户频繁操作的目的。
let timer = null;
function throttle(fn, delay = 300){
if(timer === null) {
timer = setTimeout(()=>{
fn()
clearTimeout(timer)
timer = null
}, delay)
}
}
不过我更多的是在请求之前使界面的 loading 变为 true,然后在请求结束后变为结束 loading。
# 实际需求
后端每发一次ws信息,都可能导致前端界面发送资源请求来刷新界面的数据,但是后端发送的频率太快了,有点任务执行的很快,会一秒发七八次。因此需要封装一下请求,达到以下需求:
- 在正常情况下,收到ws消息发送资源请求,随后进入锁模式
- 锁模式会在三秒后自动解锁
- 如果在锁模式的三秒内又收到ws消息,进阶到 后置请求锁模式
- 解锁时,
- 如果是锁模式,正常解锁
- 如果是后置请求锁模式,还需额外请求一次,并退阶到锁模式
export class ApiThrottle {
static NORMAL = 'normal' // 正常状态
static LOCK = 'lock' // 首次收到api请求后进入,默认锁三秒钟,期间收到api请求不会触发,会变为LOCK_END_API,一秒后自动变为NORMAL
static LOCK_END_API = 'lock_end_api' // 锁结束时,如果是当前状态,还会解锁后再请求一遍
constructor (callback, lockTime = 3000) {
this.flag = ApiThrottle.NORMAL
this.callback = callback
this.lockTime = lockTime
}
// 收到消息了,触发此函数
getRequest () {
// 正常情况下,立即触发一次请求,进入锁模式,设定三秒后进入fresh方法,自动解锁
if (this.flag === ApiThrottle.NORMAL) {
this.flag = ApiThrottle.LOCK
this.callback()
this.timeOut = setTimeout(() => { this.fresh() }, this.lockTime)
} else if (this.flag === ApiThrottle.LOCK) {
// 锁模式下进阶
this.flag = ApiThrottle.LOCK_END_API
}
}
fresh () {
if (this.flag === ApiThrottle.LOCK) {
this.flag = ApiThrottle.NORMAL
} else if (this.flag === ApiThrottle.LOCK_END_API) {
// 进阶模式,那就需要额外请求一次,并退阶
this.flag = ApiThrottle.LOCK
this.callback()
this.timeOut = setTimeout(() => { this.fresh() }, this.lockTime)
}
}
// 界面销毁时删除一切资源
destory () {
clearTimeout(this.timeOut)
delete this
}
}
# 使用方法(vue2)
{
data(){
apiThrottle: undefined,
},
mounted () {
this.apiThrottle = new ApiThrottle(() => { this.loadData(this.order) })
// 注册ws监听
this.$options.sockets.onmessage = data => {
const msg = JSON.parse(data.data)
if (msg.purpose === 'task') this.apiThrottle.getRequest()
}
},
// 销毁资源
unmounted () {
delete this.$options.sockets.onmessage
this.apiThrottle.destory()
}
}