速率限制、节流和防抖是控制函数执行频率的三种不同方法。每种技术阻止执行的方式不同,这使得它们具有“损耗性”——这意味着一些函数调用在被频繁请求时不会执行。理解何时使用每种方法对于构建高性能和可靠的应用程序至关重要。本指南将介绍 TanStack Pacer 的防抖概念。
防抖是一种技术,它会延迟函数的执行,直到发生指定的非活动时间段。与允许执行突发但有上限的速率限制,或确保执行间隔均匀的节流不同,防抖会将多个快速的函数调用合并为一次执行,该执行仅在调用停止后才发生。这使得防抖成为处理事件突发的最理想选择,因为你只关心活动停止后的最终状态。
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 提供同步和异步防抖。本指南涵盖同步的 Debouncer 类和 debounce 函数。有关异步防抖,请参阅 异步防抖指南。
debounce
函数是为任何函数添加防抖的最简单方法
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 类
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()
同步防抖器支持前置和后置边缘执行
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
})
常见模式
TanStack Pacer Debouncer 故意没有像其他防抖库那样提供 maxWait 选项。如果您需要让执行在更长的时间段内发生,请考虑使用 节流 技术。
Debouncer
类支持通过 enabled 选项启用/禁用。使用 setOptions 方法,您可以随时启用/禁用防抖器
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
选项也可以是一个返回布尔值的函数,允许根据运行时条件动态启用/禁用
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 选项设置为一个条件值,以便动态启用/禁用防抖器
// 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 中的多个选项支持通过接收防抖器实例的回调函数来实现动态值
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
}
})
以下选项支持动态值
这允许实现复杂的防抖行为,能够适应运行时条件。
同步的 Debouncer 支持以下回调
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 状态或执行清理操作非常有用。
防抖器支持刷新待定执行以立即触发它们
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 存储的位置。您必须通过提供一个选择器函数来选择加入状态跟踪。
// 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
})
)
在创建防抖器时,您可以提供初始状态值。这通常用于从持久存储中恢复状态
// 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 是响应式的并支持订阅
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 导入并使用 useStore 将 debouncer.store.state 转换为带有自定义选择器的响应式状态。
DebouncerState
包括
每个框架适配器都会围绕防抖类构建方便的 hooks 和函数。像 useDebouncer 或 createDebouncer 这样的 hooks 是小的包装器,可以在您的代码中减少某些常见用例所需的样板代码。
有关异步防抖(例如,API 调用、异步操作),请参阅 异步防抖指南。
您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。