防抖函数可同时支持立即执行(leading)和取消(cancel),关键在于用布尔标记控制首次执行、保存定时器ID并暴露cancel方法重置状态。

防抖函数中实现“立即执行”(leading edge)和“取消”(cancellation)并不冲突,关键在于控制定时器、标记状态,并暴露可调用的取消方法。
立即执行:首次调用立刻触发
标准防抖默认只在停止触发后执行,要支持首次调用立刻执行,需增加一个 leading 参数,并在第一次进入时直接执行函数,同时启动延迟定时器用于后续节流。
- 用一个布尔标记(如
isInvoked)防止重复立即执行 - 立即执行后,仍需设定时器,确保后续连续触发被抑制
- 定时器回调里重置标记,为下一次立即触发做准备
取消功能:暴露 clear 方法并清理状态
取消不是简单清掉定时器就完事——还要避免已取消的防抖再误执行,并支持多次取消与重用。
- 把
setTimeout的返回值(timerId)保存在闭包中 - 提供
cancel()方法:清除定时器 + 重置内部状态(如isInvoked = false) - 执行函数前判断是否已被取消(可选加 flag 如
isCanceled)
完整可复用实现示例
下面是一个兼顾 leading、cancel、且支持多次调用的防抖函数:
立即学习“Java免费学习笔记(深入)”;
function debounce(func, wait, options = {}) {
const { leading = false, trailing = true } = options;
let timerId = null;
let isInvoked = false;
function debounced(...args) {
const later = () => {
if (trailing && !isCanceled) {
func.apply(this, args);
}
timerId = null;
isInvoked = false;
};
const callNow = leading && !isInvoked;
if (callNow) {
func.apply(this, args);
isInvoked = true;
}
clearTimeout(timerId);
timerId = setTimeout(later, wait);
}
debounced.cancel = function() {
clearTimeout(timerId);
timerId = null;
isInvoked = false;
};
return debounced;
}
使用方式:
-
const handler = debounce(fn, 300, { leading: true })—— 首次触发立刻执行 -
handler.cancel()—— 主动取消待执行逻辑,清空状态 - 多次调用
handler()会自动覆盖上一次定时器,cancel 后再调用也正常生效
注意事项与常见坑
实际使用中容易忽略几个细节:
-
this 和参数绑定:示例中用了
func.apply(this, args),确保上下文和参数正确;若用箭头函数定义 debounced,需注意 this 绑定问题 -
cancel 后的状态残留:不清除
isInvoked,下次调用可能跳过 leading;所以 cancel 必须重置所有内部状态 - trailing 与 leading 共存逻辑:两者可同时开启,但通常互斥;若都为 true,首次立即执行 + 最后一次延迟执行,需确认业务是否需要


















