框架
版本
防抖器 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]
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 函数是为任何函数添加防抖的最简单方法

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)
})
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 时,请优先使用 useDebounceCallback hook 而非 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()
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
})
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 选项。如果您需要让执行在更长的时间段内发生,请考虑使用 节流 技术。

启用/禁用

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

ts
const debouncer = new Debouncer(fn, { wait: 500, enabled: false }) // Disable by default
debouncer.setOptions({ enabled: true }) // Enable at any time
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
  }
})
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
)
// 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
)

动态选项

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

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
  }
})
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)
  }
})
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
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 hook 的第三个参数中提供一个选择器回调,以选择加入状态跟踪,如下所示。

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

框架适配器支持 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
  })
)
// 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
})
// 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()
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 hook 已经完成了此操作。如果您需要,您也可以从 TanStack Store 导入并使用 useStoredebouncer.store.state 转换为带有自定义选择器的响应式状态。

可用状态属性

DebouncerState 包括

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

框架适配器

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


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

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

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

Bytes

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

订阅 Bytes

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

Bytes

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