一般意义上的防抖节流
函数防抖(debounce)
函数防抖,就是指触发事件后,在 n 秒后只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数的执行时间。简单的说,当一个动作连续触发,只执行最后一次。
函数节流(throttle)
限制一个函数在一定时间内只能执行一次
实现
lodash 里面提供了非常完备的实现,核心是使用定时器去延迟函数的执行
问题
- 这样的防抖节流,只能屏蔽某一个时间段的特定操作。我们的点击,往往后面是一个网络请求,如果网络请求的事件长于这个时间,那么就可能出现防抖失效的情况。持续间隔点击按钮,可能会短时间内给后端发送多个请求,如果业务场无法保证幂等,就会出现问题。
- ui 交互中,防抖实际上是延迟函数的执行,某些场景,会让人觉得卡顿,如果对交互的要求比较高,需要严格控制防抖的时间,一般不超过 200ms
如何防御
简单的实现,在业务代码中设置标志位,当有 promise 执行的时候,执行锁,等 promise 执行完毕,再释放锁。缺点,需要在业务中写很多重复的代码。
const status = false
function btnClick() {
if (status) {
return
} else {
// do something
status = ture
}
}
封装
我们可以参考防抖的实现,进行封装, 把函数用 Promise 进行包裹,然后设置标志位,就可以避免在业务中频繁设置标志位。
节流,我们可以设置函数执行的时候,同时执行一个定时器,也用 Promise 进行包装,当两个 Promise 都执行完毕的时候,再释放函数。
/**
* promise 节流函数版本
* @param fn
* @param time
* @param delayTime
* @constructor
*/
function PromiseThrottleFn(fn: Function, time = 0, delayTime = 0) {
let status = ''
return function () {
if (status === 'lock') return
status = 'lock'
try {
const data = Promise.all([
// @ts-ignore
fn.call(this, ...arguments),
new Promise((resolve) => {
setTimeout(resolve, time)
}),
])
// @ts-ignore
return data[0]
} finally {
setTimeout(() => {
status = ''
}, delayTime)
}
}
}
函数我放到了个人的工具函数库中,可以直接通过 npm 安装:
// 安装
npm install @aocoding/victorinox
// 使用
import { PromiseThrottleFn } from '@aocoding/victorinox'