Optymalizacja eventów w JavaScript: debounce i throttle
W nowoczesnych aplikacjach frontendowych płynność interfejsu i wydajność przeglądarki mają ogromne znaczenie. Wiele funkcji JavaScript wywoływanych jest w odpowiedzi na szybkie, powtarzalne akcje użytkownika, takie jak przewijanie, zmiana rozmiaru okna czy wpisywanie tekstu. Jeśli nie ograniczymy liczby tych wywołań, aplikacja może zacząć działać coraz wolniej, a interakcje staną się „ciężkie” i mało responsywne.
Właśnie dlatego powstały dwie popularne techniki optymalizacji: debounce i throttle. Choć często pojawiają się w jednym kontekście, ich działanie jest inne, a dobór właściwego narzędzia zależy od konkretnego scenariusza.
Optymalizacja eventów
Debounce – czeka, aż użytkownik skończy serię akcji
Debounce to technika, która opóźnia wykonanie funkcji aż do momentu, gdy użytkownik przerwie serie powtarzających się akcji. Każde kolejne zdarzenie resetuje licznik czasu. Funkcja zostanie wywołana dopiero wtedy, gdy nic się nie dzieje przez określony okres, np. 200 ms.
function debounce(callback, delay) {
let timeout;
return function executedFunction(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => callback.apply(this, args), delay);
};
}
debounce(myFunction, 200); // 200ms debounce
To podejście jest idealne, gdy zależy nam na końcowym efekcie działania użytkownika, a nie na każdej jego aktywności z osobna. Przykłady:
- wyszukiwanie w czasie pisania (wyślij zapytanie dopiero, gdy ktoś przestanie pisać),
- dynamiczne filtrowanie zawartości,
- obliczenia layoutu po zakończeniu zmiany rozmiaru okna,
- zapisywanie ustawień dopiero po zatrzymaniu ruchu suwaka.
Debounce ogranicza liczbę wywołań często do jednego, co znacząco odciąża aplikację.
Throttle – wykonuje się rzadziej, ale regularnie
Throttle działa inaczej: pozwala na wykonywanie funkcji nie częściej niż raz na określony interwał. Nie czeka na zakończenie akcji użytkownika, ale również nie pozwala na ciągłe, nieprzerwane wywołania. Dzięki temu otrzymujemy równowagę: funkcja wykonuje się cyklicznie, regularnie, ale nigdy zbyt często.
function throttle(callback, limit) {
let waiting = false;
return function executedFunction(...args) {
if (!waiting) {
callback.apply(this, args);
waiting = true;
setTimeout(() => {
waiting = false;
}, limit);
}
};
}
throttle(myFunction, 200); // 200ms throttle
Throttle świetnie sprawdza się w sytuacjach, gdy:
- chcemy reagować na scroll, ale nie setki razy na sekundę,
- aktualizujemy pozycje elementów podczas przewijania,
- śledzimy metryki scrolla (procent przewinięcia),
- tworzymy efekty wizualne zależne od ruchu użytkownika.
To idealne rozwiązanie, gdy zależy nam na stałych aktualizacjach, ale bez przeciążania głównego wątku.
W dużym skrócie:
Debounce – poczekaj
Throttle – kontroluj tempo
Choć debounce i throttle rozwiązują podobny problem, wybór właściwego narzędzia wpływa bezpośrednio na odbiór aplikacji przez użytkownika.
Obie techniki pozwalają znacząco zmniejszyć obciążenie przeglądarki i uniknąć sytuacji, w których interfejs „zacina się” przy dynamicznych działaniach.