Що таке "debounce" і "throttle" при запитах?
Проблема, яку вони вирішують
Якщо користувач швидко друкує в полі пошуку:
R
Re
Rea
Reac
Reactі при кожному натисканні ти відправляєш запит:
fetch(`/api/search?q=${query}`);то сервер отримає 5-10 запитів за секунду, хоча потрібен лише останній. Це:
- навантажує сервер,
- створює зайві
setStateі перемальовування, - може призвести до "гонки" даних (стара відповідь приходить пізніше нової).
Тут і допомагають debounce і throttle - способи контролювати частоту викликів функцій (наприклад, мережевих запитів).
1. Debounce (усунення тремтіння)
Debounce - відкладає виконання функції, поки користувач не перестане викликати її протягом заданого часу.
Тобто:
- кожен новий виклик скидає таймер;
- функція спрацює лише після паузи.
Приклад
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}Використання в React:
const debouncedSearch = useMemo(
() => debounce((value) => fetch(`/api/search?q=${value}`), 500),
[]
);
<input onChange={(e) => debouncedSearch(e.target.value)} />;Користувач друкує:
r → re → rea → reac → react-> fetch() виконається один раз, через 500 мс після останнього вводу.
Чудово підходить для:
- пошукових запитів;
- автокомпліту;
- фільтрації при вводі;
- перевірки унікальності імені/email при вводі.
2. Throttle (обмеження частоти)
Throttle - дозволяє виконувати функцію не частіше, ніж раз на заданий інтервал.
Тобто:
- перший виклик відбувається одразу;
- наступні ігноруються, поки не мине таймер.
Приклад
function throttle(fn, delay) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= delay) {
last = now;
fn(...args);
}
};
}Використання в React:
const throttledScroll = useMemo(
() => throttle((event) => console.log(window.scrollY), 200),
[]
);
useEffect(() => {
window.addEventListener('scroll', throttledScroll);
return () => window.removeEventListener('scroll', throttledScroll);
}, [throttledScroll]);При прокручуванні 60 разів/секунда -> обробник викличеться лише кожні 200 мс (≈5 разів/секунда).
Чудово підходить для:
- обробників
scroll,resize,mousemove; - оновлення позиції, видимості елементів;
- "infinite scroll".
Порівняння
| Особливість | Debounce | Throttle |
|---|---|---|
| Коли виконується | після паузи | через рівні інтервали |
| Поведінка при частих викликах | чекає на "тишу" | запускається періодично |
| Підходить для | пошуку, вводу тексту | скролу, ресайзу, drag-and-drop |
| Кількість викликів | мінімум | обмежена |
| UX ефект | "чекає, поки користувач закінчить" | "обмежує частоту оновлень" |
Комбінований приклад (пошук з debounce)
import { useState, useMemo, useEffect } from "react";
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
export function SearchBox() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const search = useMemo(
() => debounce(async (q) => {
const res = await fetch(`/api/search?q=${q}`);
setResults(await res.json());
}, 400),
[]
);
useEffect(() => {
if (query) search(query);
}, [query, search]);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Пошук..."
/>
<ul>{results.map(r => <li key={r.id}>{r.name}</li>)}</ul>
</div>
);
}Користувач друкує - запит виконується лише після 400 мс "тиші".
Підсумок
| Що робить | Як працює | Де використовувати |
|---|---|---|
| Debounce | чекає, поки користувач перестане викликати функцію | пошук, фільтр, валідація при вводі |
| Throttle | виконує не частіше, ніж кожні X мс | scroll, resize, drag, infinite-scroll |
| Обидва | захищають від "шторму викликів" і знижують навантаження | підвищують UX і продуктивність |
Коротко:
Debounce - "виконай, коли користувач закінчить". Throttle - "виконуй не частіше, ніж раз на N мс".
Обидва потрібні, щоб не спамити сервер і не блокувати UI зайвими рендерами при частих подіях.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.