返回
从lodash源代码解读debounce和throttle的奥秘
见解分享
2023-12-07 10:37:08
导言
lodash库中的debounce和throttle函数是处理用户输入时的重要工具。它们通过延迟函数执行或限制执行频率来优化用户体验和应用性能。本文将深入剖析这两个函数的源代码,揭示其内部机制,并探讨其应用场景和差异。
Debounce:延迟执行
debounce函数接受一个函数和一个延迟时间(以毫秒为单位)作为参数。当函数被调用时,debounce会创建一个计时器。如果在延迟时间内没有再次调用该函数,则计时器到期后会执行该函数。这种机制可以有效防止函数被频繁调用,从而提高性能并避免不必要的操作。
const debounced = _.debounce(() => {
// 此函数在延迟时间后执行
}, 500);
// 连续调用 debounced 函数
debounced();
debounced();
debounced();
// 500ms后,debounced 函数只执行一次
Throttle:限制执行频率
与debounce不同,throttle函数限制函数在指定时间间隔内只能执行一次。当函数被调用时,throttle会创建一个计时器,如果在计时器到期之前再次调用该函数,则计时器会重置。这种机制可以确保函数在指定时间间隔内只执行一次,从而限制函数的执行频率。
const throttled = _.throttle(() => {
// 此函数在指定时间间隔内只执行一次
}, 500);
// 连续调用 throttled 函数
throttled();
throttled();
throttled();
// 500ms后,throttled 函数只执行一次
源代码分析
Debounce
const debounce = (func, wait, options = {}) => {
let lastArgs, lastThis, maxWait, result, timerId, lastCallTime;
// 确保 wait 是一个正数
wait = Number(wait) || 0;
if (!options.leading) {
maxWait = wait;
} else if (options.trailing) {
maxWait = wait * 2;
} else {
maxWait = wait;
}
function delayed() {
const remaining = wait - (new Date().getTime() - lastCallTime);
if (remaining <= 0) {
if (timerId) {
clearTimeout(timerId);
}
lastArgs = lastThis = timerId = undefined;
result = func.apply(lastThis, lastArgs);
if (!timerId) {
lastCallTime = new Date().getTime();
}
} else {
timerId = setTimeout(delayed, remaining);
}
}
function cancel() {
if (timerId) {
clearTimeout(timerId);
}
lastArgs = lastThis = timerId = undefined;
}
return function debounced(...args) {
const currentThis = this;
const now = new Date().getTime();
lastArgs = args;
lastThis = currentThis;
lastCallTime = now;
// 对于 leading 为 true 的情况
if (!timerId && options.leading) {
result = func.apply(currentThis, args);
timerId = setTimeout(delayed, wait);
}
// 对于 trailing 为 true 的情况
if (timerId) {
clearTimeout(timerId);
timerId = setTimeout(delayed, maxWait - (now - lastCallTime));
}
// 对于 leading 和 trailing 均为 false 的情况
else {
if (now - lastCallTime > wait) {
timerId = setTimeout(delayed, wait);
}
}
return result;
};
};
Throttle
const throttle = (func, wait, options = {}) => {
let leading = true,
trailing = true;
if (typeof options.leading === 'boolean') {
leading = !!options.leading;
}
if (typeof options.trailing === 'boolean') {
trailing = !!options.trailing;
}
return debounce(func, wait, {
leading,
trailing,
maxWait: wait
});
};
应用场景
Debounce
- 输入框自动完成:延迟搜索请求,直到用户停止输入。
- 窗口大小改变监听器:延迟调整布局,直到窗口大小稳定。
- 滚动事件处理程序:延迟加载数据,直到滚动停止。
Throttle
- 按钮点击处理程序:限制按钮在特定时间间隔内的点击次数。
- 鼠标移动事件处理程序:限制鼠标移动事件的触发频率。
- 动画帧请求:限制动画帧的更新频率,以提高性能。
差异
特征 | Debounce | Throttle |
---|---|---|
执行时机 | 延迟 | 限制频率 |
触发方式 | 连续调用后延迟执行 | 指定时间间隔后执行 |
参数 | 函数、延迟时间、选项 | 函数、延迟时间、选项 |
应用场景 | 避免频繁调用 | 控制执行频率 |
总结
debounce和throttle函数是优化用户输入处理的宝贵工具。通过深入理解其工作原理和应用场景,开发者可以更有效地利用它们来提高应用性能和用户体验。lodash的实现提供了完善的选项,使开发者能够根据具体需求定制函数的行为。