Skip to content
On this page

throttle 节流函数

Hidden Title

基础用法:const throttled = throttle(fn, 1000)

基础用法

说明

throttle 用于创建一个节流函数,适合滚动、拖拽、窗口尺寸变化、连续点击等高频事件。它会限制函数在指定间隔内最多执行一次,并支持控制首次是否立即执行、最后一次调用是否补执行。

调用形式

ts
const throttled = throttle(fn)
const throttled = throttle(fn, delay)
const throttled = throttle(fn, delay, options)
const throttled = throttle(fn, delay, options, resultCallback)
const throttled = throttle(fn)
const throttled = throttle(fn, delay)
const throttled = throttle(fn, delay, options)
const throttled = throttle(fn, delay, options, resultCallback)

参数说明

参数类型必填默认值说明
fn(...args: any[]) => any-需要节流执行的函数。
delaynumber1000节流间隔,单位毫秒。
optionsboolean | { leading?: boolean; trailing?: boolean }{ leading: true, trailing: true }节流选项。传 false 等同于 { leading: false }
resultCallback(result) => void-每次 fn 真正执行后触发,参数是 fn 的返回值。

options 说明

字段类型默认值说明
leadingbooleantrue是否在第一次调用时立即执行。
trailingbooleantrue是否在节流周期结束时,使用最后一次调用的参数再补执行一次。

返回值

返回带 cancel()flush() 方法的节流函数:

方法说明
cancel()取消等待中的 trailing 执行,并清空当前节流状态。
flush()如果存在等待中的 trailing 执行,立即使用最后一次参数执行。

常用场景

ts
const handleResize = throttle(() => {
  console.log('resize')
}, 300)

window.addEventListener('resize', handleResize)

const saveDraft = throttle(
  (content: string) => {
    return content.trim()
  },
  1000,
  { leading: false, trailing: true },
  (result) => {
    console.log('保存草稿:', result)
  },
)

saveDraft('hello')
saveDraft.flush()
saveDraft.cancel()
const handleResize = throttle(() => {
  console.log('resize')
}, 300)

window.addEventListener('resize', handleResize)

const saveDraft = throttle(
  (content: string) => {
    return content.trim()
  },
  1000,
  { leading: false, trailing: true },
  (result) => {
    console.log('保存草稿:', result)
  },
)

saveDraft('hello')
saveDraft.flush()
saveDraft.cancel()

注意事项

throttle 默认会立即执行第一次调用,并在节流周期结束时补执行最后一次调用。若只希望在停止触发后执行一次,可以设置 { leading: false, trailing: true };若只希望固定间隔立即响应,不需要补执行,可以设置 { trailing: false }

函数源码

throttle

来源:packages/utils/src/base.ts

/**
 * 创建节流函数。
 *
 * @param fn 需要节流执行的函数。
 * @param delay 节流间隔,单位毫秒,默认 `1000`。
 * @param options 节流选项。传 `false` 等同于 `{ leading: false }`。
 * @param resultCallback 每次真正执行后触发的结果回调。
 * @returns 带 `cancel()` / `flush()` 方法的节流函数。
 *
 * @example
 * const onResize = throttle(() => {
 *   console.log('resize')
 * }, 300)
 *
 * @example
 * const save = throttle(saveDraft, 1000, { leading: false, trailing: true })
 *
 * @example
 * const track = throttle(trackPosition, 200, { trailing: false }, (result) => {
 *   console.log(result)
 * })
 *
 * track.cancel()
 * track.flush()
 */
export function throttle<T extends Func>(
  fn: T,
  delay = 1000,
  options: ThrottleOptionsInput = {},
  resultCallback?: (result: ReturnType<T>) => void,
): ThrottledFunction<T> {
  const { leading, trailing } = normalizeThrottleOptions(options)
  let timer: ReturnType<typeof setTimeout> | undefined = undefined
  let previous: number | undefined
  let lastArgs: Parameters<T> | undefined
  let lastThis: ThisParameterType<T> | undefined
  let result: ReturnType<T> | undefined

  const cancel = () => {
    if (timer) {
      clearTimeout(timer)
      timer = undefined
    }

    previous = undefined
    lastArgs = undefined
    lastThis = undefined
  }

  const invoke = () => {
    if (!lastArgs) return result

    previous = Date.now()
    const args = lastArgs
    const context = lastThis

    lastArgs = undefined
    lastThis = undefined
    const nextResult = fn.apply(context, args) as ReturnType<T>
    result = nextResult
    resultCallback?.(nextResult)
    return result
  }

  const flush = () => {
    if (timer) {
      clearTimeout(timer)
      timer = undefined
    }

    return invoke()
  }

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

    lastArgs = args
    lastThis = this

    if (previous === undefined) {
      if (leading) {
        return invoke()
      }
      previous = now
    }

    const remaining = delay - (now - previous)

    if (remaining <= 0 || remaining > delay) {
      if (timer) {
        clearTimeout(timer)
        timer = undefined
      }

      return invoke()
    }

    if (!timer && trailing) {
      timer = setTimeout(() => {
        timer = undefined

        if (!leading) {
          previous = undefined
        }

        invoke()
      }, remaining)
    }

    return result
  } as ThrottledFunction<T>

  throttled.cancel = cancel
  throttled.flush = flush

  return throttled
}

思云博智私有前端组件库