《防抖指南》中的所有核心概念同样适用于异步防抖。
通常,你可以直接使用普通的同步防抖器,它也能与异步函数正常工作。但对于高级用例,例如想要使用防抖函数的返回值(而不是仅仅调用一个setState副作用),或者将你的错误处理逻辑放在防抖器中,你可以使用异步防抖器。
TanStack Pacer 通过 AsyncDebouncer 类和 asyncDebounce 函数提供异步防抖。
以下是一个基本示例,展示了如何使用异步防抖器进行搜索操作
const debouncedSearch = asyncDebounce(
async (searchTerm: string) => {
const results = await fetchSearchResults(searchTerm)
return results
},
{
wait: 500,
onSuccess: (results, args, debouncer) => {
console.log('Search succeeded:', results)
console.log('Search arguments:', args)
},
onError: (error, args, debouncer) => {
console.error('Search failed:', error)
console.log('Failed arguments:', args)
}
}
)
// Usage
try {
const results = await debouncedSearch('query')
// Handle successful results
} catch (error) {
// Handle errors if no onError handler was provided
console.error('Search failed:', error)
}
注意:在使用 React 时,为了更好地与 React 的生命周期集成和自动清理,请优先使用 useAsyncDebouncedCallback 钩子,而不是 asyncDebounce 函数。
与返回 void 的同步防抖器不同,异步版本允许你捕获和使用防抖函数的返回值。 maybeExecute 方法返回一个 Promise,该 Promise 会使用函数的返回值解析,允许你等待结果并适当地处理它。
异步防抖器提供强大的错误处理能力
AsyncDebouncer 支持以下回调
示例
const asyncDebouncer = new AsyncDebouncer(async (value) => {
await saveToAPI(value)
}, {
wait: 500,
onSuccess: (result, args, debouncer) => {
// Called after each successful execution
console.log('Async function executed', debouncer.store.state.successCount)
console.log('Executed arguments:', args)
},
onSettled: (args, debouncer) => {
// Called after each execution attempt
console.log('Async function settled', debouncer.store.state.settleCount)
console.log('Settled arguments:', args)
},
onError: (error) => {
// Called if the async function throws an error
console.error('Async function failed:', error)
}
})
由于防抖器的 maybeExecute 方法返回一个 Promise,你可以选择在开始下一次执行之前等待每次执行。这使你能够控制执行顺序并确保每次调用处理最新的数据。这对于处理依赖于先前调用结果或在维护数据一致性至关重要的操作尤其有用。
例如,如果您正在更新用户的个人资料,然后立即获取其更新后的数据,您可以在开始获取之前等待更新操作完成。
异步防抖器通过与 AsyncRetryer 集成,包含内置的重试和中止功能。这些功能有助于处理瞬态故障并提供对正在进行的运营的控制。
使用 asyncRetryerOptions 配置失败的防抖函数执行的自动重试
const debouncedSave = asyncDebounce(
async (data: string) => {
// This might fail due to network issues
await api.save(data)
},
{
wait: 500,
asyncRetryerOptions: {
maxAttempts: 3,
backoff: 'exponential',
baseWait: 1000,
maxWait: 10000,
jitter: 0.3
}
}
)
有关重试策略、退避算法、抖动和高级重试模式的完整文档,请参阅 异步重试指南。
使用中止功能取消正在进行的防抖执行
const debouncer = new AsyncDebouncer(
async (searchTerm: string) => {
// Access the abort signal for this execution
const signal = debouncer.getAbortSignal()
if (signal) {
const response = await fetch(`/api/search?q=${searchTerm}`, { signal })
return response.json()
}
},
{ wait: 300 }
)
// Start a search
debouncer.maybeExecute('query')
// Later, abort any in-flight execution
debouncer.abort()
中止功能
有关中止模式和与 fetch/axios 集成的更多详细信息,请参阅 异步重试指南。
使用 asyncDebouncerOptions 在不同的 AsyncDebouncer 实例之间共享常用选项
import { asyncDebouncerOptions, AsyncDebouncer } from '@tanstack/pacer'
const sharedOptions = asyncDebouncerOptions({
wait: 500,
leading: false,
trailing: true,
onSuccess: (result, args, debouncer) => console.log('Success')
})
const debouncer1 = new AsyncDebouncer(fn1, { ...sharedOptions, key: 'debouncer1' })
const debouncer2 = new AsyncDebouncer(fn2, { ...sharedOptions, onError: (error) => console.error('Error') })
与同步防抖器一样,异步防抖器支持 wait 和 enabled 的动态选项,这些选项可以是接收防抖器实例的函数。这允许进行复杂且适应性强的运行时防抖行为。
异步防抖器支持刷新待处理的执行,以立即触发它们
const asyncDebouncer = new AsyncDebouncer(asyncFn, { wait: 1000 })
asyncDebouncer.maybeExecute('some-arg')
console.log(asyncDebouncer.store.state.isPending) // true
// Flush immediately instead of waiting
asyncDebouncer.flush()
console.log(asyncDebouncer.store.state.isPending) // false
AsyncDebouncer 类使用 TanStack Store 进行反应式状态管理,提供对执行状态、错误跟踪和执行统计信息的实时访问。所有状态都存储在 TanStack Store 中,可以通过 asyncDebouncer.store.state 访问,但是,如果你正在使用 React 或 Solid 等框架适配器,你将不想从这里读取状态。相反,你将从 asyncDebouncer.state 读取状态,并提供一个选择器回调作为 useAsyncDebouncer 钩子的第三个参数,以选择加入状态跟踪,如下所示。
框架适配器支持以两种方式订阅状态更改
1. 使用 asyncDebouncer.Subscribe 组件(推荐用于组件树订阅)
使用 Subscribe 组件订阅组件树深处的状态更改,而无需将选择器传递给钩子。这对于希望在子组件中订阅状态非常有用。
// Default behavior - no reactive state subscriptions at hook level
const asyncDebouncer = useAsyncDebouncer(asyncFn, { wait: 500 })
// Subscribe to state changes deep in component tree using Subscribe component
<asyncDebouncer.Subscribe selector={(state) => ({ isExecuting: state.isExecuting })}>
{(state) => (
<div>{state.isExecuting ? 'Executing...' : 'Idle'}</div>
)}
</asyncDebouncer.Subscribe>
2. 使用 selector 参数(用于钩子级别订阅)
selector 参数允许您指定哪些状态更改将触发钩子级别上的反应式更新,从而通过在发生不相关的状态更改时防止不必要的更新来优化性能。
默认情况下,asyncDebouncer.state 为空 ({}),因为选择器默认为空。 这是来自 TanStack Store useStore 的反应式状态存储的位置。你必须通过提供选择器函数来选择加入状态跟踪。
// Default behavior - no reactive state subscriptions
const asyncDebouncer = useAsyncDebouncer(asyncFn, { wait: 500 })
console.log(asyncDebouncer.state) // {}
// Opt-in to re-render when isExecuting changes
const asyncDebouncer = useAsyncDebouncer(
asyncFn,
{ wait: 500 },
(state) => ({ isExecuting: state.isExecuting })
)
console.log(asyncDebouncer.state.isExecuting) // Reactive value
// Multiple state properties
const asyncDebouncer = useAsyncDebouncer(
asyncFn,
{ wait: 500 },
(state) => ({
isExecuting: state.isExecuting,
successCount: state.successCount,
errorCount: state.errorCount
})
)
你可以在创建异步防抖器时提供初始状态值。这通常用于从持久存储恢复状态
// Load initial state from localStorage
const savedState = localStorage.getItem('async-debouncer-state')
const initialState = savedState ? JSON.parse(savedState) : {}
const asyncDebouncer = new AsyncDebouncer(asyncFn, {
wait: 500,
initialState
})
Store 是响应式的并支持订阅
const asyncDebouncer = new AsyncDebouncer(asyncFn, { wait: 500 })
// Subscribe to state changes
const unsubscribe = asyncDebouncer.store.subscribe((state) => {
// do something with the state like persist it to localStorage
})
// Unsubscribe when done
unsubscribe()
注意:在使用框架适配器时,这是不必要的,因为底层的 useStore 钩子已经执行了此操作。你还可以导入并使用 useStore 来自 TanStack Store,以将 debouncer.store.state 转换为带有自定义选择器的反应式状态,只要需要即可。
AsyncDebouncerState 包含
每个框架适配器都提供构建在核心异步防抖功能之上的钩子,以与框架的状态管理系统集成。每个框架都提供类似 createAsyncDebouncer、useAsyncDebouncedCallback 或类似的钩子。
有关核心防抖概念和同步防抖,请参阅《防抖指南》。