框架
版本
防抖器 API 参考
节流器 API 参考
速率限制器 API 参考
队列 API 参考
批处理器 API 参考

节流指南

速率限制、限流和防抖是控制函数执行频率的三种不同方法。每种技术都以不同的方式阻止执行,使它们“有损”——这意味着一些函数调用在请求过于频繁时不会执行。了解何时使用每种方法对于构建高性能和可靠的应用程序至关重要。本指南将介绍 TanStack Pacer 的限流概念。

限流概念

限流可确保函数执行在一段时间内均匀分布。与允许执行次数达到限制的速率限制不同,或等待活动停止的防抖不同,限流通过强制执行调用之间的固定延迟来创建更平滑的执行模式。如果您将限流设置为每秒一次执行,无论调用请求多么快,调用都会均匀分布。

限流可视化

text
Throttling (one execution per 3 ticks)
Timeline: [1 second per tick]
Calls:        ⬇️  ⬇️  ⬇️           ⬇️  ⬇️  ⬇️  ⬇️             ⬇️
Executed:     ✅  ❌  ⏳  ->   ✅  ❌  ❌  ❌  ✅             ✅ 
             [=================================================================]
             ^ Only one execution allowed per 3 ticks,
               regardless of how many calls are made

             [First burst]    [More calls]              [Spaced calls]
             Execute first    Execute after             Execute each time
             then throttle    wait period               wait period passes
Throttling (one execution per 3 ticks)
Timeline: [1 second per tick]
Calls:        ⬇️  ⬇️  ⬇️           ⬇️  ⬇️  ⬇️  ⬇️             ⬇️
Executed:     ✅  ❌  ⏳  ->   ✅  ❌  ❌  ❌  ✅             ✅ 
             [=================================================================]
             ^ Only one execution allowed per 3 ticks,
               regardless of how many calls are made

             [First burst]    [More calls]              [Spaced calls]
             Execute first    Execute after             Execute each time
             then throttle    wait period               wait period passes

何时使用限流

当您需要一致、可预测的执行时间时,限流尤其有效。这使其非常适合处理频繁事件或更新,您希望获得平滑、受控的行为。

何时不使用限流

当出现以下情况时,限流可能不是最佳选择

  • 您想等待活动停止(请使用 防抖
  • 您不能错过任何执行(请使用 队列

提示

当您需要平滑、一致的执行时间时,限流通常是最佳选择。它提供了比速率限制更可预测的执行模式,比防抖更即时的反馈。

TanStack Pacer 中的限流

TanStack Pacer 提供同步和异步限流。本指南涵盖同步 Throttler 类和 throttle 函数。有关异步限流,请参阅 异步限流指南

使用 throttle 的基本用法

throttle 函数是将限流添加到任何函数的最简单方法

ts
import { throttle } from '@tanstack/pacer'

// Throttle UI updates to once every 200ms
const throttledUpdate = throttle(
  (value: number) => updateProgressBar(value),
  {
    wait: 200,
  }
)

// In a rapid loop, only executes every 200ms
for (let i = 0; i < 100; i++) {
  throttledUpdate(i) // Many calls get throttled
}
import { throttle } from '@tanstack/pacer'

// Throttle UI updates to once every 200ms
const throttledUpdate = throttle(
  (value: number) => updateProgressBar(value),
  {
    wait: 200,
  }
)

// In a rapid loop, only executes every 200ms
for (let i = 0; i < 100; i++) {
  throttledUpdate(i) // Many calls get throttled
}

注意: 在使用 React 时,请优先使用 useThrottledCallback hook 而不是 throttle 函数,以更好地集成 React 的生命周期和自动清理。

使用 Throttler 类的进阶用法

为了更好地控制限流行为,您可以直接使用 Throttler

ts
import { Throttler } from '@tanstack/pacer'

const updateThrottler = new Throttler(
  (value: number) => updateProgressBar(value),
  { wait: 200 }
)

// Access current state via TanStack Store
console.log(updateThrottler.store.state.executionCount) // Number of successful executions
console.log(updateThrottler.store.state.lastExecutionTime) // Timestamp of last execution
console.log(updateThrottler.store.state.isPending) // Whether execution is pending
console.log(updateThrottler.store.state.status) // Current execution status

// Cancel any pending execution
updateThrottler.cancel()

// Flush pending execution immediately
updateThrottler.flush()
import { Throttler } from '@tanstack/pacer'

const updateThrottler = new Throttler(
  (value: number) => updateProgressBar(value),
  { wait: 200 }
)

// Access current state via TanStack Store
console.log(updateThrottler.store.state.executionCount) // Number of successful executions
console.log(updateThrottler.store.state.lastExecutionTime) // Timestamp of last execution
console.log(updateThrottler.store.state.isPending) // Whether execution is pending
console.log(updateThrottler.store.state.status) // Current execution status

// Cancel any pending execution
updateThrottler.cancel()

// Flush pending execution immediately
updateThrottler.flush()

前置和后置执行

同步限流器支持前置和后置执行

ts
const throttledFn = throttle(fn, {
  wait: 200,
  leading: true,   // Execute on first call (default)
  trailing: true,  // Execute after wait period (default)
})
const throttledFn = throttle(fn, {
  wait: 200,
  leading: true,   // Execute on first call (default)
  trailing: true,  // Execute after wait period (default)
})
  • leading: true(默认)- 首次调用时立即执行
  • leading: false - 跳过第一次调用,等待后置执行
  • trailing: true(默认)- 在等待期后执行最后一次调用
  • trailing: false - 如果在等待期内,则跳过最后一次调用

常见模式

  • { leading: true, trailing: true } - 默认,响应最快
  • { leading: false, trailing: true } - 延迟所有执行
  • { leading: true, trailing: false } - 跳过排队执行

启用/禁用

Throttler 类支持通过接收限流器实例的回调函数来启用/禁用。使用 setOptions 方法,您可以随时启用/禁用限流器

ts
const throttler = new Throttler(fn, { wait: 200, enabled: false }) // Disable by default
throttler.setOptions({ enabled: true }) // Enable at any time
const throttler = new Throttler(fn, { wait: 200, enabled: false }) // Disable by default
throttler.setOptions({ enabled: true }) // Enable at any time

enabled 选项也可以是一个返回布尔值的函数,允许根据运行时条件动态启用/禁用

ts
const throttler = new Throttler(fn, {
  wait: 200,
  enabled: (throttler) => {
    return throttler.store.state.executionCount < 50 // Disable after 50 executions
  }
})
const throttler = new Throttler(fn, {
  wait: 200,
  enabled: (throttler) => {
    return throttler.store.state.executionCount < 50 // Disable after 50 executions
  }
})

如果您使用的是框架适配器,其中限流器选项是响应式的,您可以将 enabled 选项设置为条件值,以便动态启用/禁用限流器。但是,如果您直接使用 throttle 函数或 Throttler 类,您必须使用 setOptions 方法来更改 enabled 选项,因为传递的选项实际上被传递给了 Throttler 类的构造函数。

动态选项

Throttler 中的几个选项支持通过接收限流器实例的回调函数来动态设置值

ts
const throttler = new Throttler(fn, {
  // Dynamic wait time based on execution count
  wait: (throttler) => {
    return throttler.store.state.executionCount * 100 // Increase wait time with each execution
  },
  // Dynamic enabled state based on execution count
  enabled: (throttler) => {
    return throttler.store.state.executionCount < 50 // Disable after 50 executions
  }
})
const throttler = new Throttler(fn, {
  // Dynamic wait time based on execution count
  wait: (throttler) => {
    return throttler.store.state.executionCount * 100 // Increase wait time with each execution
  },
  // Dynamic enabled state based on execution count
  enabled: (throttler) => {
    return throttler.store.state.executionCount < 50 // Disable after 50 executions
  }
})

以下选项支持动态值

  • enabled: 可以是布尔值或返回布尔值的函数
  • wait: 可以是数字或返回数字的函数

这使得可以实现复杂的限流行为,这些行为可以适应运行时条件。

回调选项

同步 Throttler 支持以下回调

ts
const throttler = new Throttler(fn, {
  wait: 200,
  onExecute: (throttler) => {
    // Called after each successful execution
    console.log('Function executed', throttler.store.state.executionCount)
  }
})
const throttler = new Throttler(fn, {
  wait: 200,
  onExecute: (throttler) => {
    // Called after each successful execution
    console.log('Function executed', throttler.store.state.executionCount)
  }
})

onExecute 回调在限流函数每次成功执行后被调用,这使其可用于跟踪执行、更新 UI 状态或执行清理操作。

刷新待定执行

限流器支持刷新待处理的执行以立即触发它们

ts
const throttler = new Throttler(fn, { wait: 1000 })

throttler.maybeExecute('some-arg')
console.log(throttler.store.state.isPending) // true

// Flush immediately instead of waiting
throttler.flush()
console.log(throttler.store.state.isPending) // false
const throttler = new Throttler(fn, { wait: 1000 })

throttler.maybeExecute('some-arg')
console.log(throttler.store.state.isPending) // true

// Flush immediately instead of waiting
throttler.flush()
console.log(throttler.store.state.isPending) // false

状态管理

Throttler 类使用 TanStack Store 进行响应式状态管理,提供对执行状态和时间信息的实时访问。所有状态都存储在 TanStack Store 中,可以通过 throttler.store.state 访问,但是,如果您使用的是 React 或 Solid 等框架适配器,则不应从此处读取状态。相反,您将从 throttler.state 读取状态,同时向 useThrottler hook 提供一个选择器回调作为第三个参数,以选择加入状态跟踪,如下所示。

状态选择器(框架适配器)

框架适配器支持一个 selector 参数,允许您指定哪些状态更改将触发重新渲染。这通过防止不相关的状态更改发生不必要的重新渲染来优化性能。

默认情况下,throttler.state 为空({}),因为选择器默认为空。 这是 TanStack Store 的响应式状态 useStore 存储的地方。您必须通过提供选择器函数来选择加入状态跟踪。

ts
// Default behavior - no reactive state subscriptions
const throttler = useThrottler(fn, { wait: 200 })
console.log(throttler.state) // {}

// Opt-in to re-render when isPending changes
const throttler = useThrottler(
  fn, 
  { wait: 200 },
  (state) => ({ isPending: state.isPending })
)
console.log(throttler.state.isPending) // Reactive value

// Multiple state properties
const throttler = useThrottler(
  fn,
  { wait: 200 },
  (state) => ({
    isPending: state.isPending,
    executionCount: state.executionCount,
    status: state.status
  })
)
// Default behavior - no reactive state subscriptions
const throttler = useThrottler(fn, { wait: 200 })
console.log(throttler.state) // {}

// Opt-in to re-render when isPending changes
const throttler = useThrottler(
  fn, 
  { wait: 200 },
  (state) => ({ isPending: state.isPending })
)
console.log(throttler.state.isPending) // Reactive value

// Multiple state properties
const throttler = useThrottler(
  fn,
  { wait: 200 },
  (state) => ({
    isPending: state.isPending,
    executionCount: state.executionCount,
    status: state.status
  })
)

初始状态

您可以在创建限流器时提供初始状态值。这通常用于从持久存储中恢复状态

ts
// Load initial state from localStorage
const savedState = localStorage.getItem('throttler-state')
const initialState = savedState ? JSON.parse(savedState) : {}

const throttler = new Throttler(fn, {
  wait: 200,
  initialState
})
// Load initial state from localStorage
const savedState = localStorage.getItem('throttler-state')
const initialState = savedState ? JSON.parse(savedState) : {}

const throttler = new Throttler(fn, {
  wait: 200,
  initialState
})

订阅状态更改

Store 是响应式的并支持订阅

ts
const throttler = new Throttler(fn, { wait: 200 })

// Subscribe to state changes
const unsubscribe = throttler.store.subscribe((state) => {
  // do something with the state like persist it to localStorage
})

// Unsubscribe when done
unsubscribe()
const throttler = new Throttler(fn, { wait: 200 })

// Subscribe to state changes
const unsubscribe = throttler.store.subscribe((state) => {
  // do something with the state like persist it to localStorage
})

// Unsubscribe when done
unsubscribe()

注意: 使用框架适配器时,此操作是多余的,因为底层的 useStore hook 已经完成了此操作。如果您需要,也可以导入并使用 TanStack Store 中的 useStorethrottler.store.state 转换为具有自定义选择器的响应式状态。

可用状态属性

ThrottlerState 包括

  • executionCount: 已完成的函数执行次数
  • isPending:节流器是否正在等待超时触发执行
  • lastArgs: 最近一次调用 maybeExecute 时的参数
  • lastExecutionTime:函数最后执行时间的戳(毫秒)
  • maybeExecuteCount: 调用 maybeExecute 的次数
  • nextExecutionTime:下次可以执行的时间戳(毫秒)
  • status: 当前执行状态(“disabled” | “idle” | “pending”)

框架适配器

每个框架适配器都围绕限流器类构建了方便的 hooks 和函数。像 useThrottlercreateThrottler 这样的 hooks 是小的包装器,可以减少在您的代码中为一些常见用例所需的样板代码。


有关异步限流(例如 API 调用、异步操作),请参阅 异步限流指南

我们的合作伙伴
Code Rabbit
Unkey
订阅 Bytes

您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。

Bytes

无垃圾邮件。您可以随时取消订阅。

订阅 Bytes

您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。

Bytes

无垃圾邮件。您可以随时取消订阅。