我做了一个修改账单功能,输入框更新之后立即保存用户输入,可以通过input标签的change事件来触发保存逻辑,可是用户输入了100个文字,那保存接口岂不是要调用100次???有没有什么办法可以确保用户输入的信息一定会保存下来,又控制住保存逻辑不要调用的那么频繁呢?有的兄弟,有的,这就是接下来要讲的 节流和防抖。
特性 | 节流(Throttle) | 防抖(Debounce) |
---|---|---|
执行时机 | 固定时间间隔执行一次 | 停止触发后延迟执行 |
响应速度 | 即时响应但频率受限 | 延迟响应 |
适用场景 | 需要规律性执行的情况(如滚动事件) | 只需最终结果的情况(如搜索输入) |
类比 | 像机枪的射速限制(每秒固定次数) | 像电梯门(等人进入后延迟关闭) |
function throttle(fn, delay) {
let timer = null;
return function(...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
}
};
}
执行逻辑:
timer
为null
,立即设置定时器delay
时间内再次调用,因timer
存在,不会执行新操作timer
,允许下一次调用function debounce(fn, delay) {
let timer = null;
return function(...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
执行逻辑:
delay
执行delay
时间内没有新调用,才会真正执行假设连续快速触发事件,delay
设为500ms:
时间轴(ms): 0---100---200---300---400---500---600---700---800
触发事件: * * * * * * * * *
节流输出: |_________|_________|_________| (每500ms固定一次)
防抖输出: |___________________| (最后一次触发后500ms)
有时需要结合两种技术:
// 首次立即执行,停止触发后再执行一次
function enhancedThrottle(fn, delay) {
let timer = null;
let lastExec = 0;
return function(...args) {
const now = Date.now();
const remaining = delay - (now - lastExec);
if (remaining <= 0) {
fn.apply(this, args);
lastExec = now;
} else {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
lastExec = Date.now();
}, remaining);
}
};
}
理解节流和防抖的区别及实现原理,能帮助你在实际开发中选择最合适的优化方案。
Lodash 提供了非常完善的节流(throttle)和防抖(debounce)功能,并且支持多种高级配置选项。
_.debounce(func, [wait=0], [options={}])
参数:
func
: 要防抖的函数wait
: 延迟的毫秒数options
: 配置对象
leading
: 指定在延迟开始前调用(默认false)trailing
: 指定在延迟结束后调用(默认true)maxWait
: 设置最大等待时间_.throttle(func, [wait=0], [options={}])
参数:
func
: 要节流的函数wait
: 节流的毫秒数options
: 配置对象
leading
: 指定在节流开始前调用(默认true)trailing
: 指定在节流结束后调用(默认true)Lodash 的 debounce
函数通过 maxWait
选项可以实现类似节流+防抖的效果:
// 结合防抖和节流的效果
// 确保至少每500ms执行一次,但最后一次调用后也会执行
const combined = _.debounce(func, 250, {
maxWait: 500,
leading: true,
trailing: true
});
这种配置实现了:
const search = _.debounce((query) => {
console.log(`Searching for: ${query}`);
// 实际搜索API调用
}, 300);
// 用户输入时
search('a');
search('ab');
search('abc'); // 只有这个会执行
const handleScroll = _.throttle(() => {
console.log('Handling scroll');
// 滚动处理逻辑
}, 100);
window.addEventListener('scroll', handleScroll);
const saveContent = _.debounce(
(content) => {
console.log('Saving:', content);
// 保存API调用
},
1000, // 防抖延迟1秒
{
maxWait: 5000, // 但最多每5秒必须保存一次
leading: false,
trailing: true
}
);
// 用户编辑时
contentEditor.on('change', saveContent);
Lodash 的节流和防抖函数返回的函数还提供了额外方法:
const throttled = _.throttle(/* ... */);
// 取消延迟的函数调用
throttled.cancel();
// 立即调用被延迟的函数
throttled.flush();
// 检查是否有等待执行的函数调用
throttled.pending();
Lodash 的这些函数非常适合处理复杂的节流和防抖需求,特别是在需要同时考虑即时响应和性能优化的场景下。