魂斗罗中的“防抖”和“节流”
2019, Apr 15
“防抖”和“节流”本质上都是避免高频函数的多次调用,大家小时候肯定玩过一款魂斗罗的游戏,其中子弹各有特点,在不考虑子弹威力,只看射击频率,假设每按下一次子弹键,触发一次发射事件。
L型:需要在按下子弹键后,松开按键一段时间,子弹才能发射出去
这就涉及到了“防抖”的概念,“防抖”指的是n
秒内函数只被调用一次,且每次的调用都重新计算时间,实现起来也很简单:
function debounce (func, delay) {
let timeoutId
const debounced = (...params) => {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => {
func(...params)
}, delay)
}
return debounced
}
function shoot (i) {
console.log(Date.now(), 'L子弹发射被执行',i)
// ... 射击的逻辑
}
const debounced = debounce(shoot, 1000) // L型子弹只有松开按键1秒后才能发射出去
for (let i = 0; i < 5; i++) {
// 假设你以每秒5下的频率按下发射键
setTimeout(() => {
console.log(Date.now(), 'L发射事件', i)
debounced(i)
}, i * 200)
}
你快速按下5次发射键,只窜出去一根小火苗,“防抖”的重点是在触发事件停止n秒后才去执行动作,在这之前的触发事件都将被抑制。
M型:发射间隔固定500ms
M型子弹发射间隔固定,每秒至多2发,“节流”指的是n秒内执行一次,执行一次后,只有大于执行周期,才会执行第二次
function throttle (func, interval) {
let timeoutId, preExecuteAt = Date.now()
const handle = (...params) => {
func(...params)
preExecuteAt = Date.now()
}
const throttled = (...params) => {
clearTimeout(timeoutId)
const waited = Date.now() - preExecuteAt
if (waited >= interval) {
handle(...params)
} else {
timeoutId = setTimeout(() => {
handle(...params)
}, interval - waited)
}
}
return throttled
}
function shoot (i) {
console.log(Date.now(), 'M子弹发射被执行',i)
// ... 射击的逻辑
}
const throttled = throttle(shoot, 500) // M型子弹发射间隔0.5秒,每秒最多发射2发
for (let i = 0; i < 5; i++) {
// 假设你以每秒5下的频率按下发射键
setTimeout(() => {
console.log(Date.now(), 'M发射事件', i)
throttled(i)
}, i * 200)
}
throttle 会强制函数以固定速率执行,每次间隔500ms以上
“防抖”和”节流”有着各自的应用场景
- 防抖:文本输入做表单验证(异步ajax,做一次验证就好)
- 节流:动画渲染