rateLimitRateLimiter KlasseRate Limiting, Throttling und Debouncing sind drei unterschiedliche Ansätze zur Steuerung der Ausführungshäufigkeit von Funktionen. Jede Technik blockiert Ausführungen auf unterschiedliche Weise, wodurch sie "verlustbehaftet" sind – das bedeutet, dass einige Funktionsaufrufe nicht ausgeführt werden, wenn sie zu häufig angefordert werden. Das Verständnis, wann welcher Ansatz verwendet werden sollte, ist entscheidend für den Aufbau leistungsfähiger und zuverlässiger Anwendungen. Diese Anleitung behandelt die Rate Limiting-Konzepte von TanStack Pacer.
Hinweis
TanStack Pacer ist derzeit ausschließlich eine Front-End-Bibliothek. Dies sind Hilfsmittel für clientseitiges Rate Limiting.
Rate Limiting ist eine Technik, die die Rate begrenzt, mit der eine Funktion über ein bestimmtes Zeitfenster ausgeführt werden kann. Es ist besonders nützlich für Szenarien, in denen Sie verhindern möchten, dass eine Funktion zu häufig aufgerufen wird, z. B. bei der Verarbeitung von API-Anfragen oder anderen externen Dienstaufrufen. Es ist der *naivste* Ansatz, da er Ausführungen in Bursts zulässt, bis das Kontingent erreicht ist.
Rate Limiting (limit: 3 calls per window)
Timeline: [1 second per tick]
Window 1 | Window 2
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Executed: ✅ ✅ ✅ ❌ ❌ ✅ ✅
[=== 3 allowed ===][=== blocked until window ends ===][=== new window =======]
Rate Limiting (limit: 3 calls per window)
Timeline: [1 second per tick]
Window 1 | Window 2
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Executed: ✅ ✅ ✅ ❌ ❌ ✅ ✅
[=== 3 allowed ===][=== blocked until window ends ===][=== new window =======]
TanStack Pacer unterstützt zwei Arten von Rate Limiting-Fenstern
Festes Fenster (Standard)
Gleitendes Fenster
Hier ist eine Visualisierung des gleitenden Fenster-Rate-Limitings
Sliding Window Rate Limiting (limit: 3 calls per window)
Timeline: [1 second per tick]
Window 1 | Window 2
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Executed: ✅ ✅ ✅ ❌ ✅ ✅ ✅
[=== 3 allowed ===][=== oldest expires, new allowed ===][=== continues sliding =======]
Sliding Window Rate Limiting (limit: 3 calls per window)
Timeline: [1 second per tick]
Window 1 | Window 2
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Executed: ✅ ✅ ✅ ❌ ✅ ✅ ✅
[=== 3 allowed ===][=== oldest expires, new allowed ===][=== continues sliding =======]
Der Hauptunterschied besteht darin, dass bei einem gleitenden Fenster eine neue Ausführung zugelassen wird, sobald die älteste abgelaufen ist. Dies schafft einen konsistenteren Ausführungsfluss im Vergleich zum Ansatz mit festen Fenstern.
Rate Limiting ist besonders wichtig, wenn es um Front-End-Operationen geht, die Ihre Back-End-Dienste versehentlich überlasten oder Leistungsprobleme im Browser verursachen könnten.
Rate Limiting ist der naivste Ansatz zur Steuerung der Ausführungshäufigkeit von Funktionen. Es ist der am wenigsten flexible und am restriktivsten der drei Techniken. Erwägen Sie die Verwendung von Throttling oder Debouncing stattdessen für stärker verteilte Ausführungen.
Tipp
Sie möchten "Rate Limiting" wahrscheinlich nicht für die meisten Anwendungsfälle verwenden. Erwägen Sie stattdessen die Verwendung von Throttling oder Debouncing.
Die "verlustbehaftete" Natur von Rate Limiting bedeutet auch, dass einige Ausführungen abgelehnt und verloren gehen. Dies kann ein Problem sein, wenn Sie sicherstellen müssen, dass alle Ausführungen immer erfolgreich sind. Erwägen Sie die Verwendung von Queuing, wenn Sie sicherstellen müssen, dass alle Ausführungen in eine Warteschlange gestellt werden, aber mit einer gedrosselten Verzögerung, um die Ausführungsrate zu verlangsamen.
TanStack Pacer bietet sowohl synchrones als auch asynchrones Rate Limiting. Diese Anleitung behandelt die synchrone RateLimiter-Klasse und die Funktion rateLimit. Für asynchrones Rate Limiting siehe die Async Rate Limiting Guide.
Die Funktion rateLimit ist die einfachste Möglichkeit, jeder Funktion Rate Limiting hinzuzufügen. Sie ist perfekt für die meisten Anwendungsfälle, bei denen Sie nur eine einfache Begrenzung erzwingen müssen.
import { rateLimit } from '@tanstack/pacer'
// Rate limit API calls to 5 per minute
const rateLimitedApi = rateLimit(
(id: string) => fetchUserData(id),
{
limit: 5,
window: 60 * 1000, // 1 minute in milliseconds
windowType: 'fixed', // default
onReject: (rateLimiter) => {
console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`)
}
}
)
// First 5 calls will execute immediately
rateLimitedApi('user-1') // ✅ Executes
rateLimitedApi('user-2') // ✅ Executes
rateLimitedApi('user-3') // ✅ Executes
rateLimitedApi('user-4') // ✅ Executes
rateLimitedApi('user-5') // ✅ Executes
rateLimitedApi('user-6') // ❌ Rejected until window resets
import { rateLimit } from '@tanstack/pacer'
// Rate limit API calls to 5 per minute
const rateLimitedApi = rateLimit(
(id: string) => fetchUserData(id),
{
limit: 5,
window: 60 * 1000, // 1 minute in milliseconds
windowType: 'fixed', // default
onReject: (rateLimiter) => {
console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`)
}
}
)
// First 5 calls will execute immediately
rateLimitedApi('user-1') // ✅ Executes
rateLimitedApi('user-2') // ✅ Executes
rateLimitedApi('user-3') // ✅ Executes
rateLimitedApi('user-4') // ✅ Executes
rateLimitedApi('user-5') // ✅ Executes
rateLimitedApi('user-6') // ❌ Rejected until window resets
Hinweis: Bei Verwendung von React bevorzugen Sie den Hook useRateLimitedCallback gegenüber der Funktion rateLimit, um eine bessere Integration mit dem Lebenszyklus von React und die automatische Bereinigung zu gewährleisten.
Für komplexere Szenarien, in denen Sie zusätzliche Kontrolle über das Rate Limiting-Verhalten benötigen, können Sie die RateLimiter-Klasse direkt verwenden. Dies gibt Ihnen Zugriff auf zusätzliche Methoden und Zustandsinformationen.
import { RateLimiter } from '@tanstack/pacer'
// Create a rate limiter instance
const limiter = new RateLimiter(
(id: string) => fetchUserData(id),
{
limit: 5,
window: 60 * 1000,
onExecute: (rateLimiter) => {
console.log('Function executed', rateLimiter.store.state.executionCount)
},
onReject: (rateLimiter) => {
console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`)
}
}
)
// Access current state via TanStack Store
console.log(limiter.getRemainingInWindow()) // Number of calls remaining in current window
console.log(limiter.store.state.executionCount) // Total number of successful executions
console.log(limiter.store.state.rejectionCount) // Total number of rejected executions
// Attempt to execute (returns boolean indicating success)
limiter.maybeExecute('user-1')
// Update options dynamically
limiter.setOptions({ limit: 10 }) // Increase the limit
// Reset all counters and state
limiter.reset()
import { RateLimiter } from '@tanstack/pacer'
// Create a rate limiter instance
const limiter = new RateLimiter(
(id: string) => fetchUserData(id),
{
limit: 5,
window: 60 * 1000,
onExecute: (rateLimiter) => {
console.log('Function executed', rateLimiter.store.state.executionCount)
},
onReject: (rateLimiter) => {
console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`)
}
}
)
// Access current state via TanStack Store
console.log(limiter.getRemainingInWindow()) // Number of calls remaining in current window
console.log(limiter.store.state.executionCount) // Total number of successful executions
console.log(limiter.store.state.rejectionCount) // Total number of rejected executions
// Attempt to execute (returns boolean indicating success)
limiter.maybeExecute('user-1')
// Update options dynamically
limiter.setOptions({ limit: 10 }) // Increase the limit
// Reset all counters and state
limiter.reset()
Die RateLimiter-Klasse unterstützt das Aktivieren/Deaktivieren über die Option enabled. Mit der Methode setOptions können Sie den Rate Limiter jederzeit aktivieren/deaktivieren.
Hinweis
Die Option enabled aktiviert/deaktiviert die eigentliche Ausführung der Funktion. Das Deaktivieren des Rate Limiters schaltet nicht das Rate Limiting aus, es verhindert nur die Ausführung der Funktion überhaupt.
const limiter = new RateLimiter(fn, {
limit: 5,
window: 1000,
enabled: false // Disable by default
})
limiter.setOptions({ enabled: true }) // Enable at any time
const limiter = new RateLimiter(fn, {
limit: 5,
window: 1000,
enabled: false // Disable by default
})
limiter.setOptions({ enabled: true }) // Enable at any time
Die Option enabled kann auch eine Funktion sein, die einen booleschen Wert zurückgibt, was dynamisches Aktivieren/Deaktivieren basierend auf Laufzeitbedingungen ermöglicht.
const limiter = new RateLimiter(fn, {
limit: 5,
window: 1000,
enabled: (limiter) => {
return limiter.store.state.executionCount < 100 // Disable after 100 executions
}
})
const limiter = new RateLimiter(fn, {
limit: 5,
window: 1000,
enabled: (limiter) => {
return limiter.store.state.executionCount < 100 // Disable after 100 executions
}
})
Wenn Sie einen Framework-Adapter verwenden, bei dem die Optionen des Rate Limiters reaktiv sind, können Sie die Option enabled auf einen bedingten Wert setzen, um den Rate Limiter dynamisch zu aktivieren/deaktivieren. Wenn Sie jedoch die Funktion rateLimit oder die Klasse RateLimiter direkt verwenden, müssen Sie die Methode setOptions verwenden, um die Option enabled zu ändern, da die übergebenen Optionen tatsächlich an den Konstruktor der Klasse RateLimiter übergeben werden.
Mehrere Optionen in der RateLimiter unterstützen dynamische Werte durch Callback-Funktionen, die die RateLimiter-Instanz erhalten
const limiter = new RateLimiter(fn, {
// Dynamic limit based on execution count
limit: (limiter) => {
return Math.max(1, 10 - limiter.store.state.executionCount) // Decrease limit with each execution
},
// Dynamic window based on execution count
window: (limiter) => {
return limiter.store.state.executionCount * 1000 // Increase window with each execution
},
// Dynamic enabled state based on execution count
enabled: (limiter) => {
return limiter.store.state.executionCount < 100 // Disable after 100 executions
}
})
const limiter = new RateLimiter(fn, {
// Dynamic limit based on execution count
limit: (limiter) => {
return Math.max(1, 10 - limiter.store.state.executionCount) // Decrease limit with each execution
},
// Dynamic window based on execution count
window: (limiter) => {
return limiter.store.state.executionCount * 1000 // Increase window with each execution
},
// Dynamic enabled state based on execution count
enabled: (limiter) => {
return limiter.store.state.executionCount < 100 // Disable after 100 executions
}
})
Die folgenden Optionen unterstützen dynamische Werte
Dies ermöglicht ein ausgeklügeltes Rate Limiting-Verhalten, das sich an Laufzeitbedingungen anpasst.
Die synchrone RateLimiter unterstützt die folgenden Callbacks
const limiter = new RateLimiter(fn, {
limit: 5,
window: 1000,
onExecute: (rateLimiter) => {
// Called after each successful execution
console.log('Function executed', rateLimiter.store.state.executionCount)
},
onReject: (rateLimiter) => {
// Called when an execution is rejected
console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`)
}
})
const limiter = new RateLimiter(fn, {
limit: 5,
window: 1000,
onExecute: (rateLimiter) => {
// Called after each successful execution
console.log('Function executed', rateLimiter.store.state.executionCount)
},
onReject: (rateLimiter) => {
// Called when an execution is rejected
console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`)
}
})
Der Callback onExecute wird nach jeder erfolgreichen Ausführung der rate-limited Funktion aufgerufen, während der Callback onReject aufgerufen wird, wenn eine Ausführung aufgrund von Rate Limiting abgelehnt wird. Diese Callbacks sind nützlich für die Nachverfolgung von Ausführungen, die Aktualisierung des UI-Zustands oder die Bereitstellung von Feedback für Benutzer.
Die Klasse RateLimiter verwendet TanStack Store für reaktives Zustandsmanagement und bietet Echtzeit-Zugriff auf den Ausführungsstatus, Fehlerverfolgung und Ablehnungsstatistiken. Der gesamte Zustand wird in einem TanStack Store gespeichert und kann über limiter.store.state abgerufen werden. Wenn Sie jedoch einen Framework-Adapter wie React oder Solid verwenden, sollten Sie den Zustand nicht von hier abrufen. Stattdessen lesen Sie den Zustand aus limiter.state und stellen einen Selektor-Callback als 3. Argument für den Hook useRateLimiter bereit, um die Zustandsverfolgung zu aktivieren, 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 Selektor standardmäßig leer ist. Hier wird reaktiver Zustand von einem TanStack Store useStore gespeichert. Sie müssen die Zustandsverfolgung aktivieren, indem Sie eine Selektorfunktion angeben.
// Default behavior - no reactive state subscriptions
const limiter = useRateLimiter(fn, { limit: 5, window: 1000 })
console.log(limiter.state) // {}
// Opt-in to re-render when rejectionCount changes
const limiter = useRateLimiter(
fn,
{ limit: 5, window: 1000 },
(state) => ({ rejectionCount: state.rejectionCount })
)
console.log(limiter.state.rejectionCount) // Reactive value
// Multiple state properties
const limiter = useRateLimiter(
fn,
{ limit: 5, window: 1000 },
(state) => ({
executionCount: state.executionCount,
rejectionCount: state.rejectionCount,
status: state.status
})
)
// Default behavior - no reactive state subscriptions
const limiter = useRateLimiter(fn, { limit: 5, window: 1000 })
console.log(limiter.state) // {}
// Opt-in to re-render when rejectionCount changes
const limiter = useRateLimiter(
fn,
{ limit: 5, window: 1000 },
(state) => ({ rejectionCount: state.rejectionCount })
)
console.log(limiter.state.rejectionCount) // Reactive value
// Multiple state properties
const limiter = useRateLimiter(
fn,
{ limit: 5, window: 1000 },
(state) => ({
executionCount: state.executionCount,
rejectionCount: state.rejectionCount,
status: state.status
})
)
Sie können Anfangszustandswerte beim Erstellen eines Rate Limiters angeben. Dies wird häufig verwendet, um den Zustand aus persistentem Speicher wiederherzustellen
// Load initial state from localStorage
const savedState = localStorage.getItem('rate-limiter-state')
const initialState = savedState ? JSON.parse(savedState) : {}
const limiter = new RateLimiter(fn, {
limit: 5,
window: 1000,
initialState
})
// Load initial state from localStorage
const savedState = localStorage.getItem('rate-limiter-state')
const initialState = savedState ? JSON.parse(savedState) : {}
const limiter = new RateLimiter(fn, {
limit: 5,
window: 1000,
initialState
})
Der Store ist reaktiv und unterstützt Abonnements
const limiter = new RateLimiter(fn, { limit: 5, window: 1000 })
// Subscribe to state changes
const unsubscribe = limiter.store.subscribe((state) => {
// do something with the state like persist it to localStorage
})
// Unsubscribe when done
unsubscribe()
const limiter = new RateLimiter(fn, { limit: 5, window: 1000 })
// Subscribe to state changes
const unsubscribe = limiter.store.subscribe((state) => {
// do something with the state like persist it to localStorage
})
// Unsubscribe when done
unsubscribe()
Hinweis: Dies ist bei Verwendung eines Framework-Adapters nicht notwendig, da der zugrunde liegende Hook useStore dies bereits tut. Sie können auch useStore von TanStack Store importieren und verwenden, um rateLimiter.store.state bei Bedarf mit einem benutzerdefinierten Selektor in reaktiven Zustand zu verwandeln.
Die RateLimiterState beinhaltet
Der Rate Limiter stellt Hilfsmethoden zur Verfügung, die Werte basierend auf dem aktuellen Zustand berechnen
const limiter = new RateLimiter(fn, { limit: 5, window: 1000 })
// These methods use the current state to compute values
console.log(limiter.getRemainingInWindow()) // Number of calls remaining in current window
console.log(limiter.getMsUntilNextWindow()) // Milliseconds until next window
const limiter = new RateLimiter(fn, { limit: 5, window: 1000 })
// These methods use the current state to compute values
console.log(limiter.getRemainingInWindow()) // Number of calls remaining in current window
console.log(limiter.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 baut praktische Hooks und Funktionen um die Rate Limiter-Klassen. Hooks wie useRateLimiter oder createRateLimiter sind kleine Wrapper, die den Boilerplate-Code für einige gängige Anwendungsfälle reduzieren können.
Für asynchrones Rate Limiting (z. B. API-Aufrufe, asynchrone Operationen) siehe die Async Rate Limiting Guide.
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.