对于高频触发在前端来说是一直存在的问题,通常我们是采取了两种处理方式:debounce和throttling。
debounce 防抖
防抖策略是指当事件被触发时设定一个周期延迟执行动作,若期间又被触发则重新设定周期,直到周期结束再执行动作。
防抖又分两种:延迟debounce和前缘debounce
- 延迟debounce: 在周期结束后再执行动作
- 前缘debounce: 在触发时就执行动作
const debounce = (func, wait, immediate = false) => {
let timer, timeStamp = 0;
let context, args;
const getNow = () => (new Date()).getTime();
const run = (timerInterval) => {
timer = setTimeout(() => {
let now = getNow();
let interval = now - timeStamp;
if (interval < timerInterval) { // 还在周期时间内,便重置初始化事件并重新执行timeOut
timeStamp = now;
run(wait - interval)
} else { // 周期已结束则触发执行函数,清除timeout
if (!immediate) { // 延迟debounce 周期结束才执行
func.apply(context, args)
}
clearTimeout(timer);
timer = null
}
}, timerInterval);
}
return function() {
context = this;
args = arguments;
let now = getNow()
timeStamp = now
if(!timer) {
if (immediate) { // 前缘debounce 周期开始才执行
fn.apply(context, args)
}
run(wait)
}
}
}
throttling 节流
节流策略是指在固定周期内,只执行一次动作,如果有新事件触发,则不执行。等下一个事件周期开始,如果有事件触发,才会执行下一次动作。 节流也分为两种:延迟throttling和前缘throttling
- 前缘throttling: 触发时就执行动作,然后下一个周期开始时再触发下一次动作
- 延迟throttling: 周期结束时再执行动作,然后下一个周期结束时再触发下一次动作
/**
* func: 执行函数
* wait: 防抖动时间间隔
* immediate:false -> 延迟debounce true -> 前缘debounce
**/
const throttling = (func, wait, immediate) => {
let timer, timeStamp = 0;
let context, args;
let run = () => {
timer = setTimeout(() => {
if (!immediate) {
func.apply(context, args);
}
clearTimeout(timer); // 周期结束,清除计时器,重置null。以便下一次直接触发函数
timer = null;
}, wait);
}
return function() {
context = this;
args = arguments;
if (!timer) { // 没有计时器则执行触发函数
if (immediate) {
func.apply(context, args)
}
run()
}
// 有计时器则不做任何操作
}
}