Zum Inhalt springen

Das Magazin für Web-Handwerker Mittwoch, 16. September 2026

Frontend

Debouncing und Throttling in JavaScript: die Ereignisrate zähmen

Tippen, Scrollen, Größenänderung: Manche Ereignisse feuern Dutzende Male pro Sekunde und überlasten den Hauptthread. Debouncing und Throttling bändigen diese Rate, jeweils für einen eigenen Bedarf, den man auseinanderhalten sollte.

Debouncing et throttling en JavaScript

Ein Suchfeld, das bei jedem Tastendruck den Server abfragt, ein Scroll-Handler, der bei jedem Pixel eine Position neu berechnet: Solche Muster lösen Hunderte überflüssiger Aufrufe aus und verschlechtern die gefühlte Reaktionsfähigkeit. Debouncing und Throttling sind die beiden klassischen Antworten. Sie ähneln sich, lösen aber unterschiedliche Probleme, und wer sie verwechselt, bekommt Oberflächen, die entweder zu träge oder zu nervös sind.

Ein Symptom, zwei Strategien

Die gemeinsame Prämisse: Ein Ereignis wiederholt sich weit schneller als die Arbeit, die man daran hängen will. Der Unterschied liegt im gewählten Zeitpunkt des Handelns. Debouncing wartet auf eine Pause, bevor die Arbeit einmal ausgeführt wird. Throttling lässt in regelmäßigen Abständen einen Aufruf durch und verwirft den Rest. Das erste optimiert auf „nur der letzte Wert zählt“, das zweite auf „ein kontrollierter Takt genügt“.

Debouncing: erst handeln, wenn Ruhe einkehrt

Ein Debounce setzt bei jedem Aufruf einen Timer zurück und führt die Funktion nur aus, wenn während der Verzögerung kein neuer Aufruf kam. Das ist das erwartete Verhalten einer Autovervollständigung: Die Anfrage startet erst, wenn der Nutzer aufhört zu tippen.

function debounce(fn, delay = 300) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

const search = debounce((query) => {
  fetch(`/api/search?q=${encodeURIComponent(query)}`);
}, 300);

input.addEventListener('input', (e) => search(e.target.value));

Die Verzögerung einzustellen ist ein Kompromiss: zu kurz, und das Warten bringt nichts; zu lang, und die Oberfläche wirkt leblos. Zwischen 200 und 400 Millisekunden deckt die meisten Eingabefelder ab. Solche Hilfsfunktionen sollten typisiert werden, damit die umschlossene Funktion ihre Signatur behält.

Throttling: einen gleichmäßigen Takt sichern

Ein Throttle führt die Funktion sofort aus und blockiert dann jeden neuen Aufruf für ein bestimmtes Fenster. Es passt zu kontinuierlichen Strömen, bei denen Zwischenwerte zählen: Scrollen, Mausbewegung, Größenänderung.

function throttle(fn, interval = 200) {
  let last = 0;
  return function (...args) {
    const now = Date.now();
    if (now - last >= interval) {
      last = now;
      fn.apply(this, args);
    }
  };
}

const onScroll = throttle(() => {
  updateScrollProgress(window.scrollY);
}, 200);

window.addEventListener('scroll', onScroll, { passive: true });

Für rein visuelle Arbeit bietet requestAnimationFrame ein natürliches Throttling im Takt der Bildwiederholrate und vermeidet, öfter zu rechnen, als der Browser zeichnet.

KriteriumDebouncingThrottling
Zeitpunkt der AusführungNach einer PauseIn regelmäßigen Abständen
Anzahl der AufrufeEiner, am EndeEiner pro Zeitfenster
Maßgeblicher WertDer letzteAuch die Zwischenwerte
Typischer FallAutovervollständigung, FormularvalidierungScrollen, Größenänderung, Ziehen

Debounce wartet auf das Ende; Throttle gibt den Takt vor. Das eine fürs andere zu wählen heißt, das falsche Problem zu lösen.

Häufige Fallstricke und gute Praxis

Die geregelte Funktion muss einmal erstellt werden, nicht bei jedem Render: In einer Komponente setzt eine neue Instanz pro Zyklus den Timer zurück und hebt den ganzen Effekt auf. Ebenso sollte man den Listener beim Unmount entfernen und für ein Debounce eine Abbruchmethode vorsehen, wenn der Kontext verschwindet, etwa beim Schließen eines Feldes.

Entscheidungshilfe. Zählt nur der Endwert, genügt ein Debounce. Braucht es kontinuierliche, aber begrenzte Rückmeldung, ein Throttle. Und speist die Berechnung nur die Anzeige, macht requestAnimationFrame beide oft überflüssig.

Schließlich sollte man die Implementierung verstehen, bevor man von einer Bibliothek abhängt. Die lodash-Varianten bieten feine Optionen, etwa das Auslösen an der führenden und der nachlaufenden Kante des Fensters, doch eine Handvoll eigener Zeilen deckt die meisten Fälle ab und spart eine Abhängigkeit für ein Verhalten, das man beherrscht.

Das Wichtigste in Kürze

Debouncing und Throttling sind keine Gegensätze, sie ergänzen sich. Das erste bündelt einen Schwall von Ereignissen zu einer Schlussaktion; das zweite glättet einen kontinuierlichen Strom zu einem gleichmäßigen Takt. Der richtige Reflex ist, zuerst den Bedarf zu benennen — letzter Wert oder begrenzte Kadenz — und dann die Technik zu wählen, nicht umgekehrt. Für reine Anzeigearbeit bleibt die Planung pro Bild das sparsamste Werkzeug.

In meinen eigenen Projekten lassen sich neun von zehn Regelungen mit einem Dutzend eigener Zeilen lösen, ohne Abhängigkeit. Zur Bibliothek greife ich nur, wenn ich führende und nachlaufende Kante zugleich brauche, und dann lohnt es sich nicht, das neu zu schreiben. Die eigentliche Falle, die mich am meisten Debugging-Zeit gekostet hat, bleibt die bei jedem Render neu erzeugte Funktion. — Simon Janvier

Zum Weiterlesen: die MDN-Referenz zu requestAnimationFrame.

Teilen LinkedIn Bluesky Hacker News E-mail

Ebenfalls lesenswert