来自速率限制指南的所有核心概念也适用于异步速率限制。
通常,您只需使用普通的同步速率限制器,它就可以与异步函数一起工作。但对于高级用例,例如想要使用速率限制函数的返回值(而不仅仅是调用 setState 侧效应),或将错误处理逻辑放在速率限制器中,您可以使用异步速率限制器。
TanStack Pacer 通过 AsyncRateLimiter 类和 asyncRateLimit 函数提供异步速率限制。
下面是一个展示如何为 API 操作使用异步速率限制器的基本示例
const rateLimitedApi = asyncRateLimit(
async (id: string) => {
const response = await fetch(`/api/data/${id}`)
return response.json()
},
{
limit: 5,
window: 1000,
onExecute: (limiter) => {
console.log('API call succeeded:', limiter.store.state.successCount)
},
onReject: (limiter) => {
console.log(`Rate limit exceeded. Try again in ${limiter.getMsUntilNextWindow()}ms`)
},
onError: (error, limiter) => {
console.error('API call failed:', error)
}
}
)
// Usage
try {
const result = await rateLimitedApi('123')
// Handle successful result
} catch (error) {
// Handle errors if no onError handler was provided
console.error('API call failed:', error)
}
const rateLimitedApi = asyncRateLimit(
async (id: string) => {
const response = await fetch(`/api/data/${id}`)
return response.json()
},
{
limit: 5,
window: 1000,
onExecute: (limiter) => {
console.log('API call succeeded:', limiter.store.state.successCount)
},
onReject: (limiter) => {
console.log(`Rate limit exceeded. Try again in ${limiter.getMsUntilNextWindow()}ms`)
},
onError: (error, limiter) => {
console.error('API call failed:', error)
}
}
)
// Usage
try {
const result = await rateLimitedApi('123')
// Handle successful result
} catch (error) {
// Handle errors if no onError handler was provided
console.error('API call failed:', error)
}
注意: 在使用 React 时,优先使用 useAsyncRateLimitedCallback hook 而不是 asyncRateLimit 函数,以更好地与 React 的生命周期集成并实现自动清理。
与返回布尔值表示成功的同步速率限制器不同,异步版本允许您捕获并使用速率限制函数的返回值。maybeExecute 方法返回一个 Promise,该 Promise 解析为函数的返回值,允许您等待结果并进行适当处理。
异步速率限制器提供强大的错误处理能力
AsyncRateLimiter 支持以下回调函数:
异步和同步速率限制器都支持 onReject 回调函数,用于处理被阻止的执行。
示例
const asyncLimiter = new AsyncRateLimiter(async (id) => {
await saveToAPI(id)
}, {
limit: 5,
window: 1000,
onExecute: (rateLimiter) => {
// Called after each successful execution
console.log('Async function executed', rateLimiter.store.state.successCount)
},
onReject: (rateLimiter) => {
// Called when an execution is rejected
console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`)
},
onError: (error) => {
// Called if the async function throws an error
console.error('Async function failed:', error)
}
})
const asyncLimiter = new AsyncRateLimiter(async (id) => {
await saveToAPI(id)
}, {
limit: 5,
window: 1000,
onExecute: (rateLimiter) => {
// Called after each successful execution
console.log('Async function executed', rateLimiter.store.state.successCount)
},
onReject: (rateLimiter) => {
// Called when an execution is rejected
console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`)
},
onError: (error) => {
// Called if the async function throws an error
console.error('Async function failed:', error)
}
})
由于速率限制器的 maybeExecute 方法返回一个 Promise,您可以选择在开始下一次执行之前等待每次执行。这让您能够控制执行顺序,并确保每次调用都处理最新的数据。这在处理依赖于先前调用结果的操作或在维护数据一致性至关重要时特别有用。
例如,如果您正在更新用户的个人资料,然后立即获取其更新后的数据,您可以在开始获取之前等待更新操作完成。
与同步速率限制器一样,异步速率限制器支持 limit、window 和 enabled 的动态选项,这些选项可以是接收速率限制器实例的函数。这允许进行复杂、运行时自适应的速率限制行为。
AsyncRateLimiter 类使用 TanStack Store 进行响应式状态管理,提供对执行状态、错误跟踪和拒绝统计的实时访问。所有状态都存储在 TanStack Store 中,可以通过 asyncLimiter.store.state 访问。但是,如果您使用的是 React 或 Solid 等框架适配器,您将不会从这里读取状态。相反,您将从 asyncLimiter.state 读取状态,并通过为 useAsyncRateLimiter hook 提供第三个参数(选择器回调)来选择加入状态跟踪,如下所示。
框架适配器支持一个 selector 参数,允许您指定哪些状态更改将触发重新渲染。这可以通过防止不相关的状态更改发生不必要的重新渲染来优化性能。
默认情况下,rateLimiter.state 为空({}),因为选择器默认为空。 这是 TanStack Store 的响应式状态 useStore 存储的位置。您必须提供一个选择器函数来选择加入状态跟踪。
// Default behavior - no reactive state subscriptions
const asyncLimiter = useAsyncRateLimiter(asyncFn, { limit: 5, window: 1000 })
console.log(asyncLimiter.state) // {}
// Opt-in to re-render when isExecuting changes
const asyncLimiter = useAsyncRateLimiter(
asyncFn,
{ limit: 5, window: 1000 },
(state) => ({ isExecuting: state.isExecuting })
)
console.log(asyncLimiter.state.isExecuting) // Reactive value
// Multiple state properties
const asyncLimiter = useAsyncRateLimiter(
asyncFn,
{ limit: 5, window: 1000 },
(state) => ({
isExecuting: state.isExecuting,
successCount: state.successCount,
errorCount: state.errorCount
})
)
// Default behavior - no reactive state subscriptions
const asyncLimiter = useAsyncRateLimiter(asyncFn, { limit: 5, window: 1000 })
console.log(asyncLimiter.state) // {}
// Opt-in to re-render when isExecuting changes
const asyncLimiter = useAsyncRateLimiter(
asyncFn,
{ limit: 5, window: 1000 },
(state) => ({ isExecuting: state.isExecuting })
)
console.log(asyncLimiter.state.isExecuting) // Reactive value
// Multiple state properties
const asyncLimiter = useAsyncRateLimiter(
asyncFn,
{ limit: 5, window: 1000 },
(state) => ({
isExecuting: state.isExecuting,
successCount: state.successCount,
errorCount: state.errorCount
})
)
您可以在创建异步速率限制器时提供初始状态值。
const savedState = localStorage.getItem('async-rate-limiter-state')
const initialState = savedState ? JSON.parse(savedState) : {}
const asyncLimiter = new AsyncRateLimiter(asyncFn, {
limit: 5,
window: 1000,
initialState
})
const savedState = localStorage.getItem('async-rate-limiter-state')
const initialState = savedState ? JSON.parse(savedState) : {}
const asyncLimiter = new AsyncRateLimiter(asyncFn, {
limit: 5,
window: 1000,
initialState
})
Store 是响应式的并支持订阅
const asyncLimiter = new AsyncRateLimiter(asyncFn, { limit: 5, window: 1000 })
// Subscribe to state changes
const unsubscribe = asyncLimiter.store.subscribe((state) => {
// do something with the state like persist it to localStorage
})
// Unsubscribe when done
unsubscribe()
const asyncLimiter = new AsyncRateLimiter(asyncFn, { limit: 5, window: 1000 })
// Subscribe to state changes
const unsubscribe = asyncLimiter.store.subscribe((state) => {
// do something with the state like persist it to localStorage
})
// Unsubscribe when done
unsubscribe()
注意: 在使用框架适配器时,这没有必要,因为底层的 useStore hook 已经完成了此操作。您也可以从 TanStack Store 导入和使用 useStore,以便在需要时将 rateLimiter.store.state 转换为具有自定义选择器的响应式状态。
AsyncRateLimiterState 包括:
异步速率限制器提供辅助方法,这些方法根据当前状态计算值。
const asyncLimiter = new AsyncRateLimiter(asyncFn, { limit: 5, window: 1000 })
// These methods use the current state to compute values
console.log(asyncLimiter.getRemainingInWindow()) // Number of calls remaining in current window
console.log(asyncLimiter.getMsUntilNextWindow()) // Milliseconds until next window
const asyncLimiter = new AsyncRateLimiter(asyncFn, { limit: 5, window: 1000 })
// These methods use the current state to compute values
console.log(asyncLimiter.getRemainingInWindow()) // Number of calls remaining in current window
console.log(asyncLimiter.getMsUntilNextWindow()) // Milliseconds until next window
这些方法是计算值,它们使用当前状态,不需要通过 store 访问。
每个框架适配器都提供基于核心异步速率限制功能构建的 hook,以集成到框架的状态管理系统中。每个框架都提供诸如 createAsyncRateLimiter、useAsyncRateLimitedCallback 或类似的 hook。
有关核心速率限制概念和同步速率限制,请参阅速率限制指南。
您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。