节流函数的“尾随执行”指触发停止后补调最后一次未执行的调用,避免遗漏操作终态;其实现需记录上次时间、维护定时器并在间隔后兜底执行,支持leading、trailing及flush/cancel。

节流函数的“尾随执行”是指:在触发停止后,如果最后一次触发未被执行,则补上一次调用。这能避免因节流间隔过长导致用户操作(如滚动、鼠标移动)结束时的最后状态被遗漏。
核心思路:记录上次触发时间 + 延迟执行最后一次
标准节流只控制“固定间隔内最多执行一次”,但不保证最后一次一定执行。要支持尾随(trailing),需额外维护一个定时器,在每次触发时清除旧的,并在间隔后尝试执行;同时,在停止触发后,再设一个延迟来兜底执行最后一次。
带尾随执行的节流函数实现
以下是一个可直接使用的版本,支持 leading(首次立即执行)、trailing(末次补执行)及取消功能:
function throttle(func, delay, options = {}) {
const { leading = true, trailing = true } = options;
let lastExecTime = 0;
let timer = null;
let lastArgs = null;
let lastThis = null;
<p>function execute() {
if (!lastArgs) return;
func.apply(lastThis, lastArgs);
lastArgs = null;
lastThis = null;
}</p><p>function throttled(...args) {
const now = Date.now();
const remaining = delay - (now - lastExecTime);</p><pre class="brush:php;toolbar:false;">lastArgs = args;
lastThis = this;
if (remaining delay) {
// 可执行:要么已超时,要么时钟回拨
if (leading) {
func.apply(this, args);
lastExecTime = now;
} else if (trailing) {
// 非 leading 模式下,先记下,等后续 flush
clearTimeout(timer);
timer = setTimeout(execute, delay);
}
} else if (trailing && !timer) {
// 还没到时间,但需要尾随 → 设一个延迟执行最后一次
timer = setTimeout(execute, remaining);
}}
// 手动触发剩余调用(例如解绑事件前) throttled.flush = () => { if (timer) { clearTimeout(timer); timer = null; } execute(); };
// 取消所有待执行(清空状态) throttled.cancel = () => { if (timer) { clearTimeout(timer); timer = null; } lastArgs = null; };
return throttled; }
使用示例与说明
比如监听窗口大小变化,希望既不频繁响应,又不错过最终尺寸:
- 启用 trailing:确保 resize 结束后仍执行一次
- 搭配 flush:在页面卸载前手动补调用,避免状态丢失
- 注意 this 和参数传递:内部正确保存了上下文与实参
调用方式:
const handleResize = throttle(() => {
console.log('窗口尺寸更新', window.innerWidth, window.innerHeight);
}, 200, { leading: false, trailing: true });
<p>window.addEventListener('resize', handleResize);</p><p>// 卸载前(如 React useEffect cleanup)
// handleResize.flush(); // 确保最后一次生效
</p>对比防抖与节流的适用场景
防抖适合“等用户彻底停手再响应”(如搜索框输入);节流适合“匀速采样+保留终点”(如滚动位置上报、canvas 绘图)。带 trailing 的节流,本质是两者的折中——它既限频,又不丢终态。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











