throttleThrottler-KlasseRate Limiting, Throttling und Debouncing sind drei unterschiedliche Ansätze zur Kontrolle der Häufigkeit von Funktionsausführungen. Jede Technik blockiert Ausführungen unterschiedlich, wodurch sie "verlustbehaftet" sind – das bedeutet, dass einige Funktionsaufrufe nicht ausgeführt werden, wenn sie zu häufig angefordert werden. Das Verständnis, wann jeder Ansatz verwendet werden soll, ist entscheidend für den Aufbau performanter und zuverlässiger Anwendungen. Diese Anleitung behandelt die Throttling-Konzepte von TanStack Pacer.
Throttling stellt sicher, dass Funktionsausführungen zeitlich gleichmäßig verteilt sind. Im Gegensatz zu Rate Limiting, das Ausführungsstöße bis zu einem Limit zulässt, oder Debouncing, das auf das Ende der Aktivität wartet, schafft Throttling ein gleichmäßigeres Ausführungsmuster, indem es konsistente Verzögerungen zwischen Aufrufen erzwingt. Wenn Sie ein Throttling von einer Ausführung pro Sekunde einstellen, werden die Aufrufe gleichmäßig verteilt, unabhängig davon, wie schnell sie angefordert werden.
Throttling (one execution per 3 ticks)
Timeline: [1 second per tick]
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Executed: ✅ ❌ ⏳ -> ✅ ❌ ❌ ❌ ✅ ✅
[=================================================================]
^ Only one execution allowed per 3 ticks,
regardless of how many calls are made
[First burst] [More calls] [Spaced calls]
Execute first Execute after Execute each time
then throttle wait period wait period passes
Throttling (one execution per 3 ticks)
Timeline: [1 second per tick]
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Executed: ✅ ❌ ⏳ -> ✅ ❌ ❌ ❌ ✅ ✅
[=================================================================]
^ Only one execution allowed per 3 ticks,
regardless of how many calls are made
[First burst] [More calls] [Spaced calls]
Execute first Execute after Execute each time
then throttle wait period wait period passes
Throttling ist besonders effektiv, wenn Sie konsistente, vorhersehbare Ausführungszeiten benötigen. Dies macht es ideal für die Verarbeitung häufiger Ereignisse oder Aktualisierungen, bei denen Sie ein reibungsloses, kontrolliertes Verhalten wünschen.
Throttling ist möglicherweise nicht die beste Wahl, wenn
Tipp
Throttling ist oft die beste Wahl, wenn Sie ein reibungsloses, konsistentes Ausführungszeitverhalten benötigen. Es bietet ein vorhersagbareres Ausführungsmuster als Rate Limiting und eine unmittelbarere Rückmeldung als Debouncing.
TanStack Pacer bietet sowohl synchrone als auch asynchrone Throttling-Mechanismen. Diese Anleitung behandelt die synchrone Throttler-Klasse und die throttle-Funktion. Für asynchrones Throttling siehe die Async Throttling-Anleitung.
Die throttle-Funktion ist der einfachste Weg, um jeder Funktion Throttling hinzuzufügen.
import { throttle } from '@tanstack/pacer'
// Throttle UI updates to once every 200ms
const throttledUpdate = throttle(
(value: number) => updateProgressBar(value),
{
wait: 200,
}
)
// In a rapid loop, only executes every 200ms
for (let i = 0; i < 100; i++) {
throttledUpdate(i) // Many calls get throttled
}
import { throttle } from '@tanstack/pacer'
// Throttle UI updates to once every 200ms
const throttledUpdate = throttle(
(value: number) => updateProgressBar(value),
{
wait: 200,
}
)
// In a rapid loop, only executes every 200ms
for (let i = 0; i < 100; i++) {
throttledUpdate(i) // Many calls get throttled
}
Hinweis: Bei Verwendung von React sollten Sie den useThrottledCallback Hook anstelle der throttle-Funktion bevorzugen, um eine bessere Integration mit dem Lebenszyklus von React und eine automatische Bereinigung zu gewährleisten.
Für mehr Kontrolle über das Throttling-Verhalten können Sie die Throttler-Klasse direkt verwenden.
import { Throttler } from '@tanstack/pacer'
const updateThrottler = new Throttler(
(value: number) => updateProgressBar(value),
{ wait: 200 }
)
// Access current state via TanStack Store
console.log(updateThrottler.store.state.executionCount) // Number of successful executions
console.log(updateThrottler.store.state.lastExecutionTime) // Timestamp of last execution
console.log(updateThrottler.store.state.isPending) // Whether execution is pending
console.log(updateThrottler.store.state.status) // Current execution status
// Cancel any pending execution
updateThrottler.cancel()
// Flush pending execution immediately
updateThrottler.flush()
import { Throttler } from '@tanstack/pacer'
const updateThrottler = new Throttler(
(value: number) => updateProgressBar(value),
{ wait: 200 }
)
// Access current state via TanStack Store
console.log(updateThrottler.store.state.executionCount) // Number of successful executions
console.log(updateThrottler.store.state.lastExecutionTime) // Timestamp of last execution
console.log(updateThrottler.store.state.isPending) // Whether execution is pending
console.log(updateThrottler.store.state.status) // Current execution status
// Cancel any pending execution
updateThrottler.cancel()
// Flush pending execution immediately
updateThrottler.flush()
Der synchrone Throttler unterstützt sowohl führende als auch nachfolgende Ausführungen.
const throttledFn = throttle(fn, {
wait: 200,
leading: true, // Execute on first call (default)
trailing: true, // Execute after wait period (default)
})
const throttledFn = throttle(fn, {
wait: 200,
leading: true, // Execute on first call (default)
trailing: true, // Execute after wait period (default)
})
Gängige Muster
Die Throttler-Klasse unterstützt das Aktivieren/Deaktivieren über die Option enabled. Mit der Methode setOptions können Sie den Throttler jederzeit aktivieren/deaktivieren.
const throttler = new Throttler(fn, { wait: 200, enabled: false }) // Disable by default
throttler.setOptions({ enabled: true }) // Enable at any time
const throttler = new Throttler(fn, { wait: 200, enabled: false }) // Disable by default
throttler.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 throttler = new Throttler(fn, {
wait: 200,
enabled: (throttler) => {
return throttler.store.state.executionCount < 50 // Disable after 50 executions
}
})
const throttler = new Throttler(fn, {
wait: 200,
enabled: (throttler) => {
return throttler.store.state.executionCount < 50 // Disable after 50 executions
}
})
Wenn Sie einen Framework-Adapter verwenden, bei dem die Throttler-Optionen reaktiv sind, können Sie die Option enabled auf einen bedingten Wert setzen, um den Throttler dynamisch zu aktivieren/deaktivieren. Wenn Sie jedoch die throttle-Funktion oder die Throttler-Klasse 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 Throttler-Klasse übergeben werden.
Mehrere Optionen in der Throttler-Klasse unterstützen dynamische Werte durch Callback-Funktionen, die die Throttler-Instanz erhalten.
const throttler = new Throttler(fn, {
// Dynamic wait time based on execution count
wait: (throttler) => {
return throttler.store.state.executionCount * 100 // Increase wait time with each execution
},
// Dynamic enabled state based on execution count
enabled: (throttler) => {
return throttler.store.state.executionCount < 50 // Disable after 50 executions
}
})
const throttler = new Throttler(fn, {
// Dynamic wait time based on execution count
wait: (throttler) => {
return throttler.store.state.executionCount * 100 // Increase wait time with each execution
},
// Dynamic enabled state based on execution count
enabled: (throttler) => {
return throttler.store.state.executionCount < 50 // Disable after 50 executions
}
})
Die folgenden Optionen unterstützen dynamische Werte
Dies ermöglicht ein ausgeklügeltes Throttling-Verhalten, das sich an Laufzeitbedingungen anpasst.
Der synchrone Throttler unterstützt den folgenden Callback:
const throttler = new Throttler(fn, {
wait: 200,
onExecute: (throttler) => {
// Called after each successful execution
console.log('Function executed', throttler.store.state.executionCount)
}
})
const throttler = new Throttler(fn, {
wait: 200,
onExecute: (throttler) => {
// Called after each successful execution
console.log('Function executed', throttler.store.state.executionCount)
}
})
Der onExecute-Callback wird nach jeder erfolgreichen Ausführung der gedrosselten Funktion aufgerufen. Dies ist nützlich für die Verfolgung von Ausführungen, die Aktualisierung des UI-Status oder die Durchführung von Bereinigungsoperationen.
Der Throttler unterstützt das Leeren ausstehender Ausführungen, um sie sofort auszulösen.
const throttler = new Throttler(fn, { wait: 1000 })
throttler.maybeExecute('some-arg')
console.log(throttler.store.state.isPending) // true
// Flush immediately instead of waiting
throttler.flush()
console.log(throttler.store.state.isPending) // false
const throttler = new Throttler(fn, { wait: 1000 })
throttler.maybeExecute('some-arg')
console.log(throttler.store.state.isPending) // true
// Flush immediately instead of waiting
throttler.flush()
console.log(throttler.store.state.isPending) // false
Die Throttler-Klasse verwendet TanStack Store für reaktives Zustandsmanagement und bietet Echtzeit-Zugriff auf Ausführungsstatus und Zeitinformationen. Alle Zustandsdaten werden in einem TanStack Store gespeichert und sind über throttler.store.state zugänglich. Wenn Sie jedoch einen Framework-Adapter wie React oder Solid verwenden, sollten Sie den Zustand nicht von hier ablesen. Stattdessen lesen Sie den Zustand aus throttler.state und stellen einen Selector-Callback als 3. Argument für den Hook useThrottler 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 throttler.state leer ({}), da der Selector standardmäßig leer ist. Hier werden reaktive Zustände aus 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 throttler = useThrottler(fn, { wait: 200 })
console.log(throttler.state) // {}
// Opt-in to re-render when isPending changes
const throttler = useThrottler(
fn,
{ wait: 200 },
(state) => ({ isPending: state.isPending })
)
console.log(throttler.state.isPending) // Reactive value
// Multiple state properties
const throttler = useThrottler(
fn,
{ wait: 200 },
(state) => ({
isPending: state.isPending,
executionCount: state.executionCount,
status: state.status
})
)
// Default behavior - no reactive state subscriptions
const throttler = useThrottler(fn, { wait: 200 })
console.log(throttler.state) // {}
// Opt-in to re-render when isPending changes
const throttler = useThrottler(
fn,
{ wait: 200 },
(state) => ({ isPending: state.isPending })
)
console.log(throttler.state.isPending) // Reactive value
// Multiple state properties
const throttler = useThrottler(
fn,
{ wait: 200 },
(state) => ({
isPending: state.isPending,
executionCount: state.executionCount,
status: state.status
})
)
Sie können Anfangswerte für den Zustand beim Erstellen eines Throttlers angeben. Dies wird häufig verwendet, um den Zustand aus persistentem Speicher wiederherzustellen.
// Load initial state from localStorage
const savedState = localStorage.getItem('throttler-state')
const initialState = savedState ? JSON.parse(savedState) : {}
const throttler = new Throttler(fn, {
wait: 200,
initialState
})
// Load initial state from localStorage
const savedState = localStorage.getItem('throttler-state')
const initialState = savedState ? JSON.parse(savedState) : {}
const throttler = new Throttler(fn, {
wait: 200,
initialState
})
Der Store ist reaktiv und unterstützt Abonnements
const throttler = new Throttler(fn, { wait: 200 })
// Subscribe to state changes
const unsubscribe = throttler.store.subscribe((state) => {
// do something with the state like persist it to localStorage
})
// Unsubscribe when done
unsubscribe()
const throttler = new Throttler(fn, { wait: 200 })
// Subscribe to state changes
const unsubscribe = throttler.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 unnötig, da der zugrunde liegende Hook useStore dies bereits tut. Sie können auch useStore von TanStack Store importieren und verwenden, um throttler.store.state bei Bedarf mit einem benutzerdefinierten Selector in einen reaktiven Zustand umzuwandeln.
ThrottlerState beinhaltet
Jeder Framework-Adapter erstellt praktische Hooks und Funktionen um die Throttler-Klassen herum. Hooks wie useThrottler oder createThrottler sind kleine Wrapper, die den Boilerplate-Code für einige gängige Anwendungsfälle reduzieren können.
Für asynchrones Throttling (z. B. API-Aufrufe, asynchrone Operationen) siehe die Async Throttling-Anleitung.
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.