双语面试:实现一个节流函数

5 阅读2分钟

面试题 Interview Question

实现一个 throttle 函数,使其保证回调函数在指定的时间间隔内最多只执行一次。
Implement a throttle function that ensures the callback executes at most once in every specified interval.

要求 Requirements

  1. throttle(fn, wait, options) 接收三个参数:

    • fn:要节流执行的函数
      fn: the function to throttle
    • wait:等待时间(毫秒) wait: the interval in milliseconds
    • options(可选):包含 { leading, trailing } 控制首尾执行
      options (optional): an object { leading, trailing } to control execution at the start and/or end
  2. 返回一个新函数:

    • 如果 leading: true,首次调用时立即执行;否则等待首个间隔结束后执行
      If leading: true, invoke immediately on the first call; otherwise defer until the first interval ends
    • 在随后的 wait 毫秒内忽略额外调用
      Ignore additional calls during the next wait ms
    • 如果 trailing: true,在最后一次调用后再执行一次
      If trailing: true, invoke one final time after the last call

参考答案 Reference Solution

function throttle(fn, wait, options = {}) {
  let timer = null;
  let lastArgs = null;
  const { leading = true, trailing = false } = options;

  return function (...args) {
    // 立即执行 / Leading execution
    if (leading && !timer) {
      fn(...args);
    } else {
      lastArgs = args;
    }

    // 安排尾调用 / Schedule trailing execution
    if (!timer) {
      timer = setTimeout(() => {
        if (trailing && lastArgs) {
          fn(...lastArgs);
          lastArgs = null;
        }
        clearTimeout(timer);
        timer = null;
      }, wait);
    }
  };
}
  • 纯函数式风格 / Pure FP style:不使用 this,通过闭包管理定时器和参数。
    Pure FP style: no this, state managed via closures.
  • 首尾执行控制 / Leading & Trailing controlleading 控制首调用,trailing 控制结尾补调用。
    leading controls the first invocation, trailing controls the final buffered call.
  • O(1) 时间调用 / O(1) time invocation:所有逻辑仅涉及简单条件判断和 setTimeout 调度,无额外循环或复杂结构。
    O(1) time invocation: logic involves only simple checks and setTimeout scheduling, with no extra loops or complex structures.

示例 Example

const log = msg => console.log('Logged:', msg);

const throttledLog = throttle(log, 1000, { leading: true, trailing: true });

throttledLog('A');  // → Logged: A (立即执行 / immediate)  
throttledLog('B');  // → [ignored] 忽略  
throttledLog('C');  // → [ignored] 忽略  
// 1000ms 后执行 trailing → Logged: C :contentReference[oaicite:12]{index=12}

考察点 Interview Focus

  • 节流 vs 防抖 :理解两者区别及适用场景;Throttling 以固定间隔执行,Debouncing 在停止触发后执行
    Throttle vs. Debounce: Understand the differences and when to use each; implement throttling to run at fixed intervals and debouncing to run only after events have stopped firing.

  • 闭包与计时器管理 :使用闭包保存状态,正确安排和清除 setTimeout 调用
    Closures & Timer Management: Use closures to preserve internal state, and properly schedule and clear setTimeout calls.

  • 可配置性设计 :设计 leading / trailing 选项以满足不同业务需求
    Configurable API Design: Provide leading and trailing options to meet different business requirements.

  • 性能优化 :在高频事件(如 scrollresizemousemove)中保持应用流畅性
    Performance Optimization: Keep the application smooth during high-frequency events (e.g., scroll, resize, mousemove).

  • 边界情况处理 :考虑重复调用、参数转发及异常情况处理等。
    Edge Case Handling: Account for repeated invocations, forward parameters correctly, and manage exceptional scenarios.