跳到主要内容
全部已验证问答

节流如何实现

作者已验证2026-08-14 · 回答由 AI 分身生成,作者本人盖章认可

节流(throttle)是指:无论事件多高频触发,在固定时间窗口内最多执行一次。它适合 scrollresize、拖拽和动画这类需要持续响应的场景【1】【2】【6】【8】。

下面是一个支持首次立即执行(leading)和结束后补一次执行(trailing)的 TypeScript 实现:

type Throttled<T extends (...args: any[]) => void> = {
  (...args: Parameters<T>): void;
  cancel: () => void;
};

function throttle<T extends (...args: any[]) => void>(
  fn: T,
  wait = 300,
  options = { leading: true, trailing: true }
): Throttled<T> {
  let timer: ReturnType<typeof setTimeout> | undefined;
  let lastTime = 0;
  let lastArgs: Parameters<T> | undefined;
  let lastThis: ThisParameterType<T> | undefined;

  function invoke() {
    lastTime = Date.now();
    timer = undefined;

    if (lastArgs) {
      fn.apply(lastThis, lastArgs);
      lastArgs = undefined;
      lastThis = undefined;
    }
  }

  function throttled(this: ThisParameterType<T>, ...args: Parameters<T>) {
    const now = Date.now();

    // 不立即执行时,首次也进入等待
    if (!lastTime && !options.leading) {
      lastTime = now;
    }

    const remaining = wait - (now - lastTime);
    lastArgs = args;
    lastThis = this;

    // 已经过了一个时间窗口,立即执行
    if (remaining <= 0 || remaining > wait) {
      if (timer) {
        clearTimeout(timer);
        timer = undefined;
      }
      invoke();
      return;
    }

    // 窗口内触发过多:结束时只补执行最后一次
    if (!timer && options.trailing) {
      timer = setTimeout(invoke, remaining);
    }
  }

  throttled.cancel = () => {
    if (timer) clearTimeout(timer);

    timer = undefined;
    lastTime = 0;
    lastArgs = undefined;
    lastThis = undefined;
  };

  return throttled;
}

scroll 示例:

const handleScroll = throttle(() => {
  console.log('当前滚动位置:', window.scrollY);
}, 200);

window.addEventListener('scroll', handleScroll);

默认行为是:

  • 第一次触发:立即执行;
  • 接下来 200ms 内:不重复执行;
  • 如果期间持续触发:窗口结束后,使用最后一次参数再执行一次。

组件卸载或事件解绑时,记得取消尾部定时器:

window.removeEventListener('scroll', handleScroll);
handleScroll.cancel();

对这个话题还有疑问?

接着问分身