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

Debouncing-Anleitung

Ratenbegrenzung, Throttling und Debouncing sind drei verschiedene Ansätze zur Steuerung der Häufigkeit von Funktionsaufrufen. Jede Technik blockiert Ausführungen auf unterschiedliche Weise und macht sie "verlustbehaftet" – das bedeutet, dass einige Funktionsaufrufe nicht ausgeführt werden, wenn sie zu häufig angefordert werden. Das Verständnis, wann jeder Ansatz verwendet werden sollte, ist entscheidend für die Erstellung performanter und zuverlässiger Anwendungen. Dieser Leitfaden behandelt die Debouncing-Konzepte von TanStack Pacer.

Debouncing-Konzept

Debouncing ist eine Technik, die die Ausführung einer Funktion verzögert, bis eine bestimmte Zeitspanne der Inaktivität eingetreten ist. Im Gegensatz zur Ratenbegrenzung, die Ausführungsstöße bis zu einem Limit zulässt, oder zum Throttling, das gleichmäßig verteilte Ausführungen gewährleistet, fasst Debouncing mehrere schnelle Funktionsaufrufe zu einer einzigen Ausführung zusammen, die erst nach Beendigung der Aufrufe stattfindet. Dies macht Debouncing ideal für die Verarbeitung von Ereignisstößen, bei denen Sie nur am Endzustand nach dem Abklingen der Aktivität interessiert sind.

Debouncing-Visualisierung

text
Debouncing (wait: 3 ticks)
Timeline: [1 second per tick]
Calls:        ⬇️  ⬇️  ⬇️  ⬇️  ⬇️     ⬇️  ⬇️  ⬇️  ⬇️               ⬇️  ⬇️
Executed:     ❌  ❌  ❌  ❌  ❌     ❌  ❌  ❌  ⏳   ->   ✅     ❌  ⏳   ->    ✅
             [=================================================================]
                                                        ^ Executes here after
                                                         3 ticks of no calls

             [Burst of calls]     [More calls]   [Wait]      [New burst]
             No execution         Resets timer    [Delayed Execute]  [Wait] [Delayed Execute]
Debouncing (wait: 3 ticks)
Timeline: [1 second per tick]
Calls:        ⬇️  ⬇️  ⬇️  ⬇️  ⬇️     ⬇️  ⬇️  ⬇️  ⬇️               ⬇️  ⬇️
Executed:     ❌  ❌  ❌  ❌  ❌     ❌  ❌  ❌  ⏳   ->   ✅     ❌  ⏳   ->    ✅
             [=================================================================]
                                                        ^ Executes here after
                                                         3 ticks of no calls

             [Burst of calls]     [More calls]   [Wait]      [New burst]
             No execution         Resets timer    [Delayed Execute]  [Wait] [Delayed Execute]

Wann Debouncing zu verwenden ist

Debouncing ist besonders effektiv, wenn Sie auf eine "Pause" in der Aktivität warten möchten, bevor Sie handeln. Dies macht es ideal für die Verarbeitung von Benutzereingaben oder anderen schnell auslösenden Ereignissen, bei denen Sie nur am Endzustand interessiert sind.

Wann Debouncing nicht zu verwenden ist

Debouncing ist möglicherweise nicht die beste Wahl, wenn

  • Sie eine garantierte Ausführung über einen bestimmten Zeitraum benötigen (verwenden Sie stattdessen Throttling)
  • Sie sich keine Ausführung entgehen lassen können (verwenden Sie stattdessen Queuing)

Debouncing in TanStack Pacer

TanStack Pacer bietet sowohl synchrone als auch asynchrone Debouncing-Funktionen. Dieser Leitfaden behandelt die synchrone Klasse Debouncer und die Funktion debounce. Für asynchrones Debouncing siehe den Leitfaden zu Asynchronem Debouncing.

Grundlegende Verwendung mit debounce

Die Funktion debounce ist die einfachste Methode, jeder Funktion Debouncing hinzuzufügen.

ts
import { debounce } from '@tanstack/pacer'

// Debounce search input to wait for user to stop typing
const debouncedSearch = debounce(
  (searchTerm: string) => performSearch(searchTerm),
  {
    wait: 500, // Wait 500ms after last keystroke
  }
)

searchInput.addEventListener('input', (e) => {
  debouncedSearch(e.target.value)
})
import { debounce } from '@tanstack/pacer'

// Debounce search input to wait for user to stop typing
const debouncedSearch = debounce(
  (searchTerm: string) => performSearch(searchTerm),
  {
    wait: 500, // Wait 500ms after last keystroke
  }
)

searchInput.addEventListener('input', (e) => {
  debouncedSearch(e.target.value)
})

Hinweis: Bei Verwendung von React bevorzugen Sie den Hook useDebounceCallback gegenüber der Funktion debounce für eine bessere Integration mit dem Lebenszyklus von React und automatische Bereinigung.

Fortgeschrittene Verwendung mit der Debouncer-Klasse

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

ts
import { Debouncer } from '@tanstack/pacer'

const searchDebouncer = new Debouncer(
  (searchTerm: string) => performSearch(searchTerm),
  { wait: 500 }
)

// Access current state via TanStack Store
console.log(searchDebouncer.store.state.executionCount) // Number of successful executions
console.log(searchDebouncer.store.state.isPending) // Whether a call is pending
console.log(searchDebouncer.store.state.status) // Current execution status

// Update options dynamically
searchDebouncer.setOptions({ wait: 1000 }) // Increase wait time

// Cancel pending execution
searchDebouncer.cancel()

// Flush pending execution immediately
searchDebouncer.flush()
import { Debouncer } from '@tanstack/pacer'

const searchDebouncer = new Debouncer(
  (searchTerm: string) => performSearch(searchTerm),
  { wait: 500 }
)

// Access current state via TanStack Store
console.log(searchDebouncer.store.state.executionCount) // Number of successful executions
console.log(searchDebouncer.store.state.isPending) // Whether a call is pending
console.log(searchDebouncer.store.state.status) // Current execution status

// Update options dynamically
searchDebouncer.setOptions({ wait: 1000 }) // Increase wait time

// Cancel pending execution
searchDebouncer.cancel()

// Flush pending execution immediately
searchDebouncer.flush()

Führende und nachlaufende Ausführungen

Der synchrone Debouncer unterstützt sowohl führende als auch nachlaufende Ausführungen.

ts
const debouncedFn = debounce(fn, {
  wait: 500,
  leading: true,   // Execute on first call
  trailing: true,  // Execute after wait period
})
const debouncedFn = debounce(fn, {
  wait: 500,
  leading: true,   // Execute on first call
  trailing: true,  // Execute after wait period
})
  • leading: true - Die Funktion wird sofort beim ersten Aufruf ausgeführt.
  • leading: false (Standard) - Der erste Aufruf startet den Warte-Timer.
  • trailing: true (Standard) - Die Funktion wird nach der Wartezeit ausgeführt.
  • trailing: false - Keine Ausführung nach der Wartezeit.

Gängige Muster

  • { leading: false, trailing: true } - Standard, Ausführung nach der Wartezeit.
  • { leading: true, trailing: false } - Sofortige Ausführung, nachfolgende Aufrufe werden ignoriert.
  • { leading: true, trailing: true } - Ausführung sowohl beim ersten Aufruf als auch nach der Wartezeit.

Maximale Wartezeit

Der TanStack Pacer Debouncer hat absichtlich keine maxWait-Option wie andere Debouncing-Bibliotheken. Wenn Sie möchten, dass Ausführungen über einen längeren Zeitraum verteilt werden, sollten Sie stattdessen die Throttling-Technik verwenden.

Aktivieren/Deaktivieren

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

ts
const debouncer = new Debouncer(fn, { wait: 500, enabled: false }) // Disable by default
debouncer.setOptions({ enabled: true }) // Enable at any time
const debouncer = new Debouncer(fn, { wait: 500, enabled: false }) // Disable by default
debouncer.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 debouncer = new Debouncer(fn, {
  wait: 500,
  enabled: (debouncer) => {
    return debouncer.store.state.executionCount < 10 // Disable after 10 executions
  }
})
const debouncer = new Debouncer(fn, {
  wait: 500,
  enabled: (debouncer) => {
    return debouncer.store.state.executionCount < 10 // Disable after 10 executions
  }
})

Wenn Sie einen Framework-Adapter verwenden, bei dem die Debouncer-Optionen reaktiv sind, können Sie die Option enabled auf einen bedingten Wert setzen, um den Debouncer dynamisch zu aktivieren/deaktivieren.

ts
// React example
const debouncer = useDebouncer(
  setSearch, 
  { wait: 500, enabled: searchInput.value.length > 3 } // Enable/disable based on input length IF using a framework adapter that supports reactive options
)
// React example
const debouncer = useDebouncer(
  setSearch, 
  { wait: 500, enabled: searchInput.value.length > 3 } // Enable/disable based on input length IF using a framework adapter that supports reactive options
)

Dynamische Optionen

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

ts
const debouncer = new Debouncer(fn, {
  // Dynamic wait time based on execution count
  wait: (debouncer) => {
    return debouncer.store.state.executionCount * 100 // Increase wait time with each execution
  },
  // Dynamic enabled state based on execution count
  enabled: (debouncer) => {
    return debouncer.store.state.executionCount < 10 // Disable after 10 executions
  }
})
const debouncer = new Debouncer(fn, {
  // Dynamic wait time based on execution count
  wait: (debouncer) => {
    return debouncer.store.state.executionCount * 100 // Increase wait time with each execution
  },
  // Dynamic enabled state based on execution count
  enabled: (debouncer) => {
    return debouncer.store.state.executionCount < 10 // Disable after 10 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 Debouncing-Verhalten, das sich an Laufzeitbedingungen anpasst.

Callback-Optionen

Der synchrone Debouncer unterstützt den folgenden Callback.

ts
const debouncer = new Debouncer(fn, {
  wait: 500,
  onExecute: (debouncer) => {
    // Called after each successful execution
    console.log('Function executed', debouncer.store.state.executionCount)
  }
})
const debouncer = new Debouncer(fn, {
  wait: 500,
  onExecute: (debouncer) => {
    // Called after each successful execution
    console.log('Function executed', debouncer.store.state.executionCount)
  }
})

Der Callback onExecute wird nach jeder erfolgreichen Ausführung der debounced Funktion aufgerufen und ist nützlich für die Verfolgung von Ausführungen, die Aktualisierung des UI-Zustands oder die Durchführung von Bereinigungsoperationen.

Ausstehende Ausführungen abbrechen

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

ts
const debouncer = new Debouncer(fn, { wait: 1000 })

debouncer.maybeExecute('some-arg')
console.log(debouncer.store.state.isPending) // true

// Flush immediately instead of waiting
debouncer.flush()
console.log(debouncer.store.state.isPending) // false
const debouncer = new Debouncer(fn, { wait: 1000 })

debouncer.maybeExecute('some-arg')
console.log(debouncer.store.state.isPending) // true

// Flush immediately instead of waiting
debouncer.flush()
console.log(debouncer.store.state.isPending) // false

Zustandsverwaltung

Die Klasse Debouncer verwendet TanStack Store für reaktives Zustandsmanagement und bietet Echtzeit-Zugriff auf Ausführungsstatus und Statistiken. Der gesamte Zustand wird in einem TanStack Store gespeichert und kann über debouncer.store.state abgerufen werden. Wenn Sie jedoch einen Framework-Adapter wie React oder Solid verwenden, sollten Sie den Zustand nicht von hier abrufen. Stattdessen rufen Sie den Zustand von debouncer.state ab und stellen eine Selektor-Callback als 3. Argument für den Hook useDebouncer zur Verfügung, 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 debouncer.state leer ({}), da der Selektor standardmäßig leer ist. Hier wird der reaktive Zustand von einem TanStack Store useStore gespeichert. Sie müssen sich für die Zustandsverfolgung entscheiden, indem Sie eine Selektorfunktion bereitstellen.

ts
// Default behavior - no reactive state subscriptions
const debouncer = useDebouncer(fn, { wait: 500 })
console.log(debouncer.state) // {}

// Opt-in to re-render when isPending changes
const debouncer = useDebouncer(
  fn, 
  { wait: 500 },
  (state) => ({ isPending: state.isPending })
)
console.log(debouncer.state.isPending) // Reactive value

// Multiple state properties
const debouncer = useDebouncer(
  fn,
  { wait: 500 },
  (state) => ({
    isPending: state.isPending,
    executionCount: state.executionCount,
    status: state.status
  })
)
// Default behavior - no reactive state subscriptions
const debouncer = useDebouncer(fn, { wait: 500 })
console.log(debouncer.state) // {}

// Opt-in to re-render when isPending changes
const debouncer = useDebouncer(
  fn, 
  { wait: 500 },
  (state) => ({ isPending: state.isPending })
)
console.log(debouncer.state.isPending) // Reactive value

// Multiple state properties
const debouncer = useDebouncer(
  fn,
  { wait: 500 },
  (state) => ({
    isPending: state.isPending,
    executionCount: state.executionCount,
    status: state.status
  })
)

Anfangszustand

Sie können Anfangswerte für den Zustand beim Erstellen eines Debouncers angeben. Dies wird häufig verwendet, um Zustände aus persistentem Speicher wiederherzustellen.

ts
// Load initial state from localStorage
const savedState = localStorage.getItem('debouncer-state')
const initialState = savedState ? JSON.parse(savedState) : {}

const debouncer = new Debouncer(fn, {
  wait: 500,
  key: 'search-debouncer',
  initialState
})
// Load initial state from localStorage
const savedState = localStorage.getItem('debouncer-state')
const initialState = savedState ? JSON.parse(savedState) : {}

const debouncer = new Debouncer(fn, {
  wait: 500,
  key: 'search-debouncer',
  initialState
})

Zustandsänderungen abonnieren

Der Store ist reaktiv und unterstützt Abonnements

ts
const debouncer = new Debouncer(fn, { wait: 500 })

// Subscribe to state changes
const unsubscribe = debouncer.store.subscribe((state) => {
  // do something with the state like persist it to localStorage
})

// Unsubscribe when done
unsubscribe()
const debouncer = new Debouncer(fn, { wait: 500 })

// Subscribe to state changes
const unsubscribe = debouncer.store.subscribe((state) => {
  // do something with the state like persist it to localStorage
})

// Unsubscribe when done
unsubscribe()

Hinweis: Dies ist unnötig, wenn Sie einen Framework-Adapter verwenden, da der zugrunde liegende Hook useStore dies bereits tut. Sie können auch useStore von TanStack Store importieren und verwenden, um debouncer.store.state bei Bedarf in reaktiven Zustand mit einem benutzerdefinierten Selektor umzuwandeln.

Verfügbare Zustandseigenschaften

Die DebouncerState beinhaltet

  • canLeadingExecute: Ob der Debouncer auf der führenden Kante des Timeouts ausführen kann.
  • executionCount: Anzahl der abgeschlossenen Funktionsaufrufe
  • isPending: Ob der Debouncer auf den Timeout wartet, um die Ausführung auszulösen
  • lastArgs: Die Argumente des letzten Aufrufs von maybeExecute
  • maybeExecuteCount: Anzahl der Aufrufe von maybeExecute
  • status: Aktueller Ausführungsstatus ('disabled' | 'idle' | 'pending')

Framework-Adapter

Jeder Framework-Adapter erstellt praktische Hooks und Funktionen um die Debouncer-Klassen. Hooks wie useDebouncer oder createDebouncer sind kleine Wrapper, die den Boilerplate-Code für einige gängige Anwendungsfälle reduzieren können.


Für asynchrones Debouncing (z. B. API-Aufrufe, asynchrone Operationen) siehe den Leitfaden zu Asynchronem Debouncing.

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.