Framework
Version
Debouncer API Referenz
Throttler API Referenz
Rate Limiter API Referenz
Queue API Referenz
Batcher API Referenz

Throttling-Anleitung

Rate 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-Konzept

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-Visualisierung

text
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

Wann Throttling verwendet werden sollte

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.

Wann Throttling nicht verwendet werden sollte

Throttling ist möglicherweise nicht die beste Wahl, wenn

  • Sie auf das Ende der Aktivität warten möchten (verwenden Sie stattdessen Debouncing)
  • Sie keine Ausführungen verpassen können (verwenden Sie stattdessen Queuing)

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.

Throttling in TanStack Pacer

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.

Grundlegende Verwendung mit throttle

Die throttle-Funktion ist der einfachste Weg, um jeder Funktion Throttling hinzuzufügen.

ts
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.

Fortgeschrittene Verwendung mit der Throttler-Klasse

Für mehr Kontrolle über das Throttling-Verhalten können Sie die Throttler-Klasse direkt verwenden.

ts
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()

Führende und nachfolgende Ausführungen

Der synchrone Throttler unterstützt sowohl führende als auch nachfolgende Ausführungen.

ts
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)
})
  • leading: true (Standard) – Sofortige Ausführung beim ersten Aufruf.
  • leading: false – Erste Ausführung überspringen, auf nachfolgende Ausführung warten.
  • trailing: true (Standard) – Letzte Ausführung nach der Wartezeit durchführen.
  • trailing: false – Letzte Ausführung überspringen, wenn sie innerhalb der Wartezeit liegt.

Gängige Muster

  • { leading: true, trailing: true } – Standard, am reaktionsfreudigsten.
  • { leading: false, trailing: true } – Alle Ausführungen verzögern.
  • { leading: true, trailing: false } – Wartende Ausführungen überspringen.

Aktivieren/Deaktivieren

Die Throttler-Klasse unterstützt das Aktivieren/Deaktivieren über die Option enabled. Mit der Methode setOptions können Sie den Throttler jederzeit aktivieren/deaktivieren.

ts
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.

ts
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.

Dynamische Optionen

Mehrere Optionen in der Throttler-Klasse unterstützen dynamische Werte durch Callback-Funktionen, die die Throttler-Instanz erhalten.

ts
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

  • enabled: Kann ein boolescher Wert oder eine Funktion sein, die einen booleschen Wert zurückgibt.
  • wait: Kann eine Zahl oder eine Funktion sein, die eine Zahl zurückgibt

Dies ermöglicht ein ausgeklügeltes Throttling-Verhalten, das sich an Laufzeitbedingungen anpasst.

Callback-Optionen

Der synchrone Throttler unterstützt den folgenden Callback:

ts
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.

Ausstehende Ausführungen abbrechen

Der Throttler unterstützt das Leeren ausstehender Ausführungen, um sie sofort auszulösen.

ts
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

Zustandsverwaltung

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.

Zustandsselektor (Framework-Adapter)

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.

ts
// 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
  })
)

Anfangszustand

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.

ts
// 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
})

Zustandsänderungen abonnieren

Der Store ist reaktiv und unterstützt Abonnements

ts
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.

Verfügbare Zustandseigenschaften

ThrottlerState beinhaltet

  • executionCount: Anzahl der abgeschlossenen Funktionsaufrufe
  • isPending: Ob der Throttler auf den Timeout wartet, um die Ausführung auszulösen
  • lastArgs: Die Argumente des letzten Aufrufs von maybeExecute
  • lastExecutionTime: Zeitstempel der letzten Funktionsausführung in Millisekunden
  • maybeExecuteCount: Anzahl der Aufrufe von maybeExecute
  • nextExecutionTime: Zeitstempel, wann die nächste Ausführung erfolgen kann, in Millisekunden
  • status: Aktueller Ausführungsstatus ('disabled' | 'idle' | 'pending')

Framework-Adapter

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.

Unsere Partner
Code Rabbit
Unkey
Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.

Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.