如何为TypeScript节流函数添加leading选项?
问题
我有一个常用的TypeScript工具函数throttle,用于控制函数的执行频率。现在希望扩展该函数,使其支持leading选项。
Leading选项说明:
- 当
leading设为true时,函数在首次调用时立即执行,即在等待周期开始时触发。例如,为滚动事件节流的函数设置该选项后,滚动一开始就会调用该函数。
这些选项能让开发者微调函数的执行时机,适配应用中的特定场景。
当前throttle函数实现:
export function throttle(fn: (...args: any[]) => void, wait = 100) { let timeoutId: ReturnType<typeof setTimeout> | undefined; let lastTime = Date.now(); const execute = (...args: any[]) => { lastTime = Date.now(); fn(...args); }; return (...args: any[]) => { const currentTime = Date.now(); const elapsed = currentTime - lastTime; if (elapsed >= wait) { execute(...args); } else { if (timeoutId !== undefined) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { execute(...args); timeoutId = undefined; }, wait - elapsed); } }; }
需要修改该函数以支持leading选项,同时提供示例代码及实现说明。
测试代码:
const sleep = (wait: number) => new Promise((r) => setTimeout(r, wait)); (async () => { const wait = 100; const fn = ((...args: any[]) => console.log(args)); const throttled = throttle(fn, wait); throttled('a', 'b', 'c'); // 应被调用 await sleep(wait - 5); throttled('b', 'a', 'c'); await sleep(wait - 10); throttled('c', 'b', 'a'); // 也应被调用 await sleep(wait + 10); // 确保等待足够时长以触发两次调用 })();
解决方案
修改思路
原函数默认是leading: true的行为,但未暴露配置选项。要支持leading开关,需做以下调整:
- 兼容原有API的同时,新增配置参数选项
- 增加状态变量跟踪节流等待周期,避免
leading模式下重复触发立即执行 - 根据
leading选项分支处理首次调用及后续调用的执行逻辑
完整实现代码
export interface ThrottleOptions { wait?: number; leading?: boolean; } export function throttle( fn: (...args: any[]) => void, options: number | ThrottleOptions = 100 ) { // 解析参数:兼容原有的"fn + wait"调用方式,同时支持配置对象 const { wait = 100, leading = true } = typeof options === 'number' ? { wait: options } : options; let timeoutId: ReturnType<typeof setTimeout> | undefined; let lastExecuteTime = 0; // 初始化为0,方便判断是否为首次调用 let isInWaitPeriod = false; // 标记当前是否处于节流等待周期 const executeFn = (...args: any[]) => { lastExecuteTime = Date.now(); isInWaitPeriod = false; fn(...args); }; return (...args: any[]) => { const currentTime = Date.now(); const elapsed = currentTime - lastExecuteTime; if (!isInWaitPeriod) { // 非等待周期时,根据leading选项判断是否立即执行 if (leading && (lastExecuteTime === 0 || elapsed >= wait)) { executeFn(...args); isInWaitPeriod = true; } else if (!leading && elapsed >= wait) { executeFn(...args); } else { // 未满足立即执行条件,设置延迟执行定时器 if (timeoutId) clearTimeout(timeoutId); timeoutId = setTimeout(() => executeFn(...args), wait - elapsed); isInWaitPeriod = true; } } else { // 处于等待周期内,更新定时器确保最后一次调用能触发 if (timeoutId) clearTimeout(timeoutId); timeoutId = setTimeout(() => executeFn(...args), wait - elapsed); } }; }
关键修改说明
- 参数兼容:支持两种调用形式,既可以
throttle(fn, 100),也可以throttle(fn, { wait: 100, leading: false }),不破坏原有代码的兼容性。 - 状态管理:新增
isInWaitPeriod变量标记当前是否处于节流等待周期,避免leading: true模式下在等待期内重复触发立即执行。 - leading逻辑分支:
- 当
leading: true时,首次调用或等待周期结束后立即执行函数,同时进入等待状态。 - 当
leading: false时,首次调用不会立即执行,需等待第一个完整的节流周期后才触发执行。
- 当
- 定时器优化:等待周期内的新调用会清除原有定时器并重新设置,确保最后一次调用能在等待周期结束后执行。
测试验证
使用提供的测试代码:
- 默认
leading: true时,第一次调用throttled('a', 'b', 'c')会立即执行;等待95ms后调用的throttled('b', 'a', 'c')处于等待周期内,会更新定时器;再等待90ms后,距离首次执行已过185ms,超过100ms等待周期,throttled('c', 'b', 'a')会立即执行。 - 若设置
leading: false,第一次调用不会立即执行,会在100ms后触发;后续等待周期内的调用会更新定时器,最后一次调用会在等待周期结束后执行。
内容的提问来源于stack exchange,提问作者DonHunt7382
相关产品推荐
相关产品推荐

