文档
CodeRabbit
Cloudflare
AG Grid
Netlify
Neon
WorkOS
Clerk
Convex
Electric
PowerSync
Sentry
Railway
Prisma
Strapi
Unkey
CodeRabbit
Cloudflare
AG Grid
Netlify
Neon
WorkOS
Clerk
Convex
Electric
PowerSync
Sentry
Railway
Prisma
Strapi
Unkey
防抖器 API 参考
节流器 API 参考
速率限制器 API 参考
队列 API 参考
批处理器 API 参考
指南

防抖指南

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

防抖概念

防抖是一种技术,它会延迟函数的执行,直到发生指定的一段时间的非活动状态。与速率限制(允许在限制内爆发执行)或节流(确保均匀间隔的执行)不同,防抖会将多次快速函数调用合并为单个执行,该执行仅在调用停止后发生。这使得防抖非常适合处理事件爆发,在这些事件中,您只关心活动稳定后的最终状态。

防抖可视化

text
Debouncing (wait: 3 ticks)
Timeline: [1 second per tick]
Calls:        ⬇️  ⬇️  ⬇️  ⬇️  ⬇️     ⬇️  ⬇️  ⬇️  ⬇️               ⬇️  ⬇️
Executed:     ❌  ❌  ❌  ❌  ❌     ❌  ❌  ❌  ⏳   ->   ✅     ❌  ⏳   ->    ✅
             [=================================================================]
                                                        ^ Executes here after
                                                         3 ticks of no calls

             [Burst of calls]     [More calls]   [Wait]      [New burst]
             No execution         Resets timer    [Delayed Execute]  [Wait] [Delayed Execute]

何时使用防抖

当您希望在活动“暂停”后才采取行动时,防抖特别有效。这使其非常适合处理用户输入或其他快速触发的事件,在这些事件中,您只关心最终状态。

何时不使用防抖

防抖可能不是最佳选择,当

  • 您需要在特定时间段内保证执行(请改用 节流
  • 您不能承受任何执行的丢失(请改用 排队

TanStack Pacer 中的防抖

TanStack Pacer 提供同步和异步防抖。本指南涵盖同步 Debouncer 类和 debounce 函数。有关异步防抖,请参阅 异步防抖指南

使用 debounce 的基本用法

debounce 函数是将防抖添加到任何函数的 simplest 方法

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

// Debounce search input to wait for user to stop typing
const debouncedSearch = debounce(
  (searchTerm: string) => performSearch(searchTerm),
  {
    wait: 500, // Wait 500ms after last keystroke
  }
)

searchInput.addEventListener('input', (e) => {
  debouncedSearch(e.target.value)
})

注意:在使用 React 时,请优先使用 useDebouncedCallback 钩子,而不是 debounce 函数,以便更好地与 React 的生命周期集成和自动清理。

使用 Debouncer 类的进阶用法

为了更好地控制防抖行为,您可以使用 Debouncer

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

const searchDebouncer = new Debouncer(
  (searchTerm: string) => performSearch(searchTerm),
  { wait: 500 }
)

// Access current state via TanStack Store
console.log(searchDebouncer.store.state.executionCount) // Number of successful executions
console.log(searchDebouncer.store.state.isPending) // Whether a call is pending
console.log(searchDebouncer.store.state.status) // Current execution status

// Update options dynamically
searchDebouncer.setOptions({ wait: 1000 }) // Increase wait time

// Cancel pending execution
searchDebouncer.cancel()

// Flush pending execution immediately
searchDebouncer.flush()

前沿和尾随执行

同步防抖器支持前沿和尾随执行

ts
const debouncedFn = debounce(fn, {
  wait: 500,
  leading: true,   // Execute on first call
  trailing: true,  // Execute after wait period
})
  • leading: true - 函数在第一次调用时立即执行
  • leading: false (默认) - 第一次调用启动等待计时器
  • trailing: true (默认) - 函数在等待期后执行
  • trailing: false - 在等待期后不执行

常用模式

  • { leading: false, trailing: true } - 默认,等待后执行
  • { leading: true, trailing: false } - 立即执行,忽略后续调用
  • { leading: true, trailing: true } - 在第一次调用和等待后执行

最大等待时间

TanStack Pacer Debouncer 故意没有像其他防抖库那样的 maxWait 选项。如果您需要在更分散的时间段内让执行运行,请考虑使用 节流 技术。

在实例之间共享选项

使用 debouncerOptions 在不同的 Debouncer 实例之间共享常用选项

ts
import { debouncerOptions, Debouncer } from '@tanstack/pacer'

const sharedOptions = debouncerOptions({
  wait: 500,
  leading: false,
  trailing: true
})

const debouncer1 = new Debouncer(fn1, { ...sharedOptions, key: 'debouncer1' })
const debouncer2 = new Debouncer(fn2, { ...sharedOptions, wait: 1000 })

启用/禁用

Debouncer 类支持通过 enabled 选项启用/禁用。使用 setOptions 方法,您可以随时启用/禁用防抖器

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

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

ts
const debouncer = new Debouncer(fn, {
  wait: 500,
  enabled: (debouncer) => {
    return debouncer.store.state.executionCount < 10 // Disable after 10 executions
  }
})

如果您正在使用具有反应式选项的框架适配器,您可以将 enabled 选项设置为条件值,以随时启用/禁用防抖器

ts
// React example
const debouncer = useDebouncer(
  setSearch, 
  { wait: 500, enabled: searchInput.value.length > 3 } // Enable/disable based on input length IF using a framework adapter that supports reactive options
)

动态选项

防抖器中的几个选项支持通过接收防抖器实例的回调函数动态值

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

以下选项支持动态值

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

这允许根据运行时条件调整的复杂防抖行为。

回调选项

同步 Debouncer 支持以下回调

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

每次成功执行防抖函数后,都会调用 onExecute 回调,使其对于跟踪执行、更新 UI 状态或执行清理操作非常有用。

刷新待定执行

防抖器支持刷新待处理的执行,以立即触发它们

ts
const debouncer = new Debouncer(fn, { wait: 1000 })

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

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

状态管理

Debouncer 类使用 TanStack Store 进行反应式状态管理,提供对执行状态和统计信息的实时访问。所有状态都存储在 TanStack Store 中,可以通过 debouncer.store.state 访问,但是,如果您正在使用 React 或 Solid 等框架适配器,您将不想从这里读取状态。相反,您将从 debouncer.state 读取状态,并提供选择器回调作为 useDebouncer 钩子的第三个参数,以选择加入状态跟踪,如下所示。

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

框架适配器支持以两种方式订阅状态更改

1. 使用 debouncer.Subscribe 组件(推荐用于组件树订阅)

使用 Subscribe 组件订阅组件树深处的状态更改,而无需将选择器传递给钩子。这对于希望在子组件中订阅状态非常有用。

tsx
// Default behavior - no reactive state subscriptions at hook level
const debouncer = useDebouncer(fn, { wait: 500 })

// Subscribe to state changes deep in component tree using Subscribe component
<debouncer.Subscribe selector={(state) => ({ isPending: state.isPending })}>
  {(state) => (
    <div>{state.isPending ? 'Waiting...' : 'Ready'}</div>
  )}
</debouncer.Subscribe>

2. 使用 selector 参数(用于钩子级别订阅)

selector 参数允许您指定哪些状态更改将触发钩子级别上的反应式更新,从而通过在发生不相关的状态更改时防止不必要的更新来优化性能。

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

ts
// Default behavior - no reactive state subscriptions
const debouncer = useDebouncer(fn, { wait: 500 })
console.log(debouncer.state) // {}

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

// Multiple state properties
const debouncer = useDebouncer(
  fn,
  { wait: 500 },
  (state) => ({
    isPending: state.isPending,
    executionCount: state.executionCount,
    status: state.status
  })
)

初始状态

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

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

const debouncer = new Debouncer(fn, {
  wait: 500,
  key: 'search-debouncer',
  initialState
})

订阅状态更改

Store 是响应式的并支持订阅

ts
const debouncer = new Debouncer(fn, { wait: 500 })

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

// Unsubscribe when done
unsubscribe()

注意:当使用框架适配器时,这是不必要的,因为底层的 useStore 钩子已经执行了此操作。您还可以导入并使用 useStore 来自 TanStack Store,以在任何必要时将 debouncer.store.state 转换为具有自定义选择器的反应式状态。

可用状态属性

DebouncerState 包括

  • canLeadingExecute: 防抖器是否可以执行超时前沿
  • executionCount: 已完成的函数执行次数
  • isPending: 防抖器是否正在等待超时触发执行
  • lastArgs: 上次调用 maybeExecute 的参数
  • maybeExecuteCount: maybeExecute 被调用的次数
  • status: 当前执行状态('disabled' | 'idle' | 'pending')

框架适配器

每个框架适配器都在防抖器类周围构建方便的钩子和函数。像 useDebouncercreateDebouncer 这样的钩子是小的包装器,可以减少您在一些常见用例中的代码量。


对于异步防抖(例如,API 调用、异步操作),请参阅 异步防抖指南