知道。防抖(debounce)的核心是:事件频繁触发时不断重置计时器,直到停止触发超过指定时间,才执行最后一次回调。
它适合搜索联想、服务端表单校验,以及避免按钮连续点击重复提交;而 scroll、拖拽、resize 这类需要持续响应的高频事件,通常更适合节流【2】【3】【5】。
type Debounced<T extends (...args: any[]) => any> = {
(...args: Parameters<T>): void;
cancel: () => void;
};
function debounce<T extends (...args: any[]) => any>(
fn: T,
wait = 300
): Debounced<T> {
let timer: ReturnType<typeof setTimeout> | undefined;
function debounced(this: ThisParameterType<T>, ...args: Parameters<T>) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
timer = undefined;
fn.apply(this, args);
}, wait);
}
debounced.cancel = () => {
if (timer) clearTimeout(timer);
timer = undefined;
};
return debounced;
}
使用示例:
const search = debounce(async (keyword: string) => {
const res = await fetch(`/api/search?q=${encodeURIComponent(keyword)}`);
console.log(await res.json());
}, 300);
document.querySelector<HTMLInputElement>('#search')!
.addEventListener('input', (event) => {
search(event.currentTarget.value);
});
如果用户连续输入,前面的定时器都会被取消;只有停下来 300ms 后,才会用最终的关键词发一次请求。
按钮提交时可以这样用:
const submit = debounce(() => {
console.log('提交表单');
}, 500);
document.querySelector('#submit')!
.addEventListener('click', submit);
不过我补充一点:防抖只能改善前端重复触发,不能代替服务端幂等控制。 对支付、创建订单等关键接口,后端仍应通过幂等键、唯一约束或状态机保证不会重复处理。