Appearance
2676. 节流
ts
type F = (...args: number[]) => void
function throttle(fn: F, t: number): F {
let lastArgs: number[] = []
let nextRunTime = 0
let timer
return function (...args) {
const now = new Date().getTime()
if (now >= nextRunTime) {
nextRunTime = now + t
fn(...args)
} else {
clearTimeout(timer)
lastArgs = [...args]
timer = setTimeout(() => {
nextRunTime = Date.now() + t
fn(...lastArgs!)
lastArgs = []
timer = null
}, nextRunTime - now)
}
}
}
/**
* const throttled = throttle(console.log, 100);
* throttled("log"); // logged immediately.
* throttled("log"); // logged at t=100ms.
*/节流 vs 防抖
这两个概念经常会混在一起,但语义并不相同:
- 防抖(debounce):连续触发时,只执行最后一次。适合搜索框联想。
- 节流(throttle):连续触发时,保证固定间隔至少执行一次。适合滚动事件、resize。
节流的关键在于:高频调用下,执行机会不会一直被推迟到最后。
实现要点
这个实现主要处理了两件事:
首调用立即执行。 第一次调用时 now >= nextRunTime(nextRunTime 初始为 0),直接执行并设置下一次允许执行的时间。
冷却期内保存最后一次调用。 如果在冷却期内有调用,用 setTimeout 在冷却结束时执行最后一次保存的 args。如果冷却期内来了多次调用,后面的覆盖前面的——clearTimeout 清除旧定时器,lastArgs 更新为最新的参数。这保证了冷却期结束后执行的是最新的调用,而不是最早排队的那个。
这里保留最后一次调用而不是第一次调用,主要是为了避免冷却结束后执行到过时参数。
