You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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开关,需做以下调整:

  1. 兼容原有API的同时,新增配置参数选项
  2. 增加状态变量跟踪节流等待周期,避免leading模式下重复触发立即执行
  3. 根据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);
        }
    };
}

关键修改说明

  1. 参数兼容:支持两种调用形式,既可以throttle(fn, 100),也可以throttle(fn, { wait: 100, leading: false }),不破坏原有代码的兼容性。
  2. 状态管理:新增isInWaitPeriod变量标记当前是否处于节流等待周期,避免leading: true模式下在等待期内重复触发立即执行。
  3. leading逻辑分支:
    • 当leading: true时,首次调用或等待周期结束后立即执行函数,同时进入等待状态。
    • 当leading: false时,首次调用不会立即执行,需等待第一个完整的节流周期后才触发执行。
  4. 定时器优化:等待周期内的新调用会清除原有定时器并重新设置,确保最后一次调用能在等待周期结束后执行。

测试验证

使用提供的测试代码:

  • 默认leading: true时,第一次调用throttled('a', 'b', 'c')会立即执行;等待95ms后调用的throttled('b', 'a', 'c')处于等待周期内,会更新定时器;再等待90ms后,距离首次执行已过185ms,超过100ms等待周期,throttled('c', 'b', 'a')会立即执行。
  • 若设置leading: false,第一次调用不会立即执行,会在100ms后触发;后续等待周期内的调用会更新定时器,最后一次调用会在等待周期结束后执行。

内容的提问来源于stack exchange,提问作者DonHunt7382

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 00:12:02