Alle Kernkonzepte aus dem Rate Limiting Guide gelten auch für die asynchrone Ratenbegrenzung.
Normalerweise können Sie einfach den normalen synchronen Ratenbegrenzer verwenden, und er funktioniert mit asynchronen Funktionen. Für fortgeschrittene Anwendungsfälle, z. B. wenn Sie den Rückgabewert einer ratenbegrenzten Funktion verwenden möchten (anstatt nur einen setState-Seiteneffekt aufzurufen) oder Ihre Fehlerbehandlungslogik in den Ratenbegrenzer legen möchten, können Sie den asynchronen Ratenbegrenzer verwenden.
TanStack Pacer bietet asynchrone Ratenbegrenzung über die Klasse AsyncRateLimiter und die Funktion asyncRateLimit.
Hier ist ein grundlegendes Beispiel, das zeigt, wie der asynchrone Ratenbegrenzer für einen API-Vorgang verwendet wird
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)
}
Hinweis: Bei der Verwendung von React bevorzugen Sie den Hook useAsyncRateLimitedCallback gegenüber der Funktion asyncRateLimit, um eine bessere Integration mit dem Lebenszyklus von React und der automatischen Bereinigung zu gewährleisten.
Im Gegensatz zum synchronen Ratenbegrenzer, der einen booleschen Wert zurückgibt, der den Erfolg anzeigt, ermöglicht die asynchrone Version das Erfassen und Verwenden des Rückgabewerts Ihrer ratenbegrenzten Funktion. Die Methode maybeExecute gibt ein Promise zurück, das mit dem Rückgabewert der Funktion aufgelöst wird, sodass Sie auf das Ergebnis warten und es entsprechend behandeln können.
Der asynchrone Ratenbegrenzer bietet robuste Fehlerbehandlungsfunktionen
Die AsyncRateLimiter unterstützt die folgenden Callbacks
Sowohl der asynchrone als auch der synchrone Ratenbegrenzer unterstützen den onReject-Callback für die Behandlung blockierter Ausführungen.
Beispiel
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)
}
})
Da die Methode maybeExecute des Ratenbegrenzers ein Promise zurückgibt, können Sie wählen, auf jede Ausführung zu warten, bevor Sie die nächste starten. Dies gibt Ihnen die Kontrolle über die Ausführungsreihenfolge und stellt sicher, dass jeder Aufruf die aktuellsten Daten verarbeitet. Dies ist besonders nützlich, wenn Sie mit Vorgängen zu tun haben, die von den Ergebnissen früherer Aufrufe abhängen oder wenn die Aufrechterhaltung der Datenkonsistenz entscheidend ist.
Wenn Sie beispielsweise das Profil eines Benutzers aktualisieren und dann sofort dessen aktualisierte Daten abrufen, können Sie die Aktualisierungsoperation awaiten, bevor Sie den Abruf starten.
Genau wie der synchrone Ratenbegrenzer unterstützt der asynchrone Ratenbegrenzer dynamische Optionen für limit, window und enabled, die Funktionen sein können, die die Ratenbegrenzer-Instanz empfangen. Dies ermöglicht ein ausgeklügeltes, laufzeitadaptives Ratenbegrenzungsverhalten.
Die Klasse AsyncRateLimiter verwendet TanStack Store für reaktives Zustandsmanagement und bietet Echtzeit-Zugriff auf den Ausführungsstatus, die Fehlerverfolgung und Ablehnungsstatistiken. Alle Zustände werden in einem TanStack Store gespeichert und können über asyncLimiter.store.state abgerufen werden. Wenn Sie jedoch einen Framework-Adapter wie React oder Solid verwenden, möchten Sie den Zustand nicht von hier lesen. Stattdessen lesen Sie den Zustand von asyncLimiter.state und stellen einen Selector-Callback als 3. Argument für den Hook useAsyncRateLimiter bereit, um sich für die Zustandsverfolgung zu entscheiden, wie unten gezeigt.
Framework-Adapter unterstützen ein selector-Argument, das es Ihnen ermöglicht, anzugeben, welche Zustandsänderungen Neu-Renderings auslösen. Dies optimiert die Leistung, indem unnötige Neu-Renderings bei irrelevanten Zustandsänderungen verhindert werden.
Standardmäßig ist rateLimiter.state leer ({}), da der Selector standardmäßig leer ist. Hier werden reaktive Zustände von einem TanStack Store useStore gespeichert. Sie müssen sich für die Zustandsverfolgung entscheiden, indem Sie eine Selector-Funktion bereitstellen.
// 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
})
)
Sie können Anfangswerte für den Zustand beim Erstellen eines asynchronen Ratenbegrenzers angeben
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
})
Der Store ist reaktiv und unterstützt Abonnements
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()
Hinweis: Dies ist nicht erforderlich, wenn Sie einen Framework-Adapter verwenden, da der zugrunde liegende Hook useStore dies bereits tut. Sie können useStore auch aus TanStack Store importieren und verwenden, um rateLimiter.store.state bei Bedarf in reaktive Zustände mit einem benutzerdefinierten Selector umzuwandeln.
Die AsyncRateLimiterState enthält
Der asynchrone Ratenbegrenzer bietet Hilfsmethoden, die Werte basierend auf dem aktuellen Zustand berechnen
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
Diese Methoden sind berechnete Werte, die den aktuellen Zustand verwenden und nicht über den Store abgerufen werden müssen.
Jeder Framework-Adapter stellt Hooks bereit, die auf der Kernfunktionalität der asynchronen Ratenbegrenzung aufbauen, um sich in das Zustandsverwaltungssystem des Frameworks zu integrieren. Hooks wie createAsyncRateLimiter, useAsyncRateLimitedCallback oder ähnliche sind für jedes Framework verfügbar.
Für Kernkonzepte der Ratenbegrenzung und synchrone Ratenbegrenzung siehe den Rate Limiting Guide.
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.