В чем разница между Throttling и Debouncing?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
В чем разница между Throttling и Debouncing
Throttling и Debouncing — это две техники оптимизации для контроля частоты выполнения функций при частых событиях. Они решают разные проблемы.
Debouncing (Задержка выполнения)
Debouncing — это техника, которая откладывает выполнение функции до момента, когда события остановятся.
// Без debouncing
const input = document.querySelector("input");
input.addEventListener("input", (event) => {
console.log("Запрос к API:", event.target.value);
// Пользователь печатает "hello" — будет 5 запросов!
// "h" -> запрос
// "he" -> запрос
// "hel" -> запрос
// "hell" -> запрос
// "hello" -> запрос
});
// С debouncing
function debounce(func, delay) {
let timeoutId;
return function(...args) {
// Отменяем предыдущий таймер
clearTimeout(timeoutId);
// Устанавливаем новый таймер
timeoutId = setTimeout(() => {
func(...args);
}, delay);
};
}
const debouncedSearch = debounce((value) => {
console.log("Запрос к API:", value);
}, 500);
input.addEventListener("input", (event) => {
debouncedSearch(event.target.value);
// Пользователь печатает "hello" — только 1 запрос!
// После того как пользователь прекратит печать на 500ms
});
Диаграмма debouncing:
События: |---input-|---input-|---input-|---STOP---| (500ms)
|
v
Выполнение: ---------------------------EXECUTE---------
Характеристики:
- Функция выполняется один раз после окончания событий
- Идеален для автосохранения, поиска, валидации
- Экономит ресурсы
- Может вызвать задержку в отклике
Throttling (Ограничение частоты)
Throttling — это техника, которая выполняет функцию максимум один раз в определённый период времени, независимо от того, сколько событий произойдёт.
// Без throttling
window.addEventListener("scroll", () => {
console.log("Скролл");
// Может вызваться 100+ раз в секунду!
});
// С throttling
function throttle(func, delay) {
let lastCallTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastCallTime >= delay) {
lastCallTime = now;
func(...args);
}
};
}
const throttledScroll = throttle(() => {
console.log("Скролл");
}, 1000);
window.addEventListener("scroll", throttledScroll);
// Максимум один раз в 1000ms
Диаграмма throttling:
События: |-----input-----|-----input-----|-----input-----|
| | | | | | |
v v v v v v v
Выполнение: EXEC--SKIP--SKIP--EXEC--SKIP--SKIP--EXEC------
(каждые 1000ms)
Характеристики:
- Функция выполняется периодически (например, каждые N миллисекунд)
- Гарантированное выполнение с постоянной частотой
- Идеален для скролла, изменения размера окна, движения мыши
- Лучше реактивность
Прямое сравнение
| Параметр | Debouncing | Throttling |
|---|---|---|
| Выполнение | После окончания событий | Периодически |
| Частота | Один раз | Несколько раз на интервал |
| Задержка | Может быть задержка | Предсказуемая частота |
| Луч.сценарий | Поиск, автосохранение | Скролл, resize, mousemove |
| Производительность | Отличная (мало вызовов) | Хорошая (управляемые вызовы) |
Практические примеры
Debouncing: Поиск с API
const searchInput = document.querySelector("#search");
const searchResults = document.querySelector("#results");
const debouncedSearch = debounce(async (query) => {
if (query.length < 2) return;
const response = await fetch(`/api/search?q=${query}`);
const results = await response.json();
searchResults.innerHTML = results
.map(item => `<li>${item.name}</li>`)
.join("");
}, 300);
searchInput.addEventListener("input", (e) => {
debouncedSearch(e.target.value);
});
Результат: При печати "javascript" будет только 1 запрос вместо 10.
Throttling: Обработка скролла
function throttle(func, delay) {
let lastCallTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastCallTime >= delay) {
lastCallTime = now;
func(...args);
}
};
}
const throttledScroll = throttle(() => {
const scrollPos = window.scrollY;
console.log("Scroll position:", scrollPos);
// Загрузить больше контента если пользователь близко к концу
if (scrollPos + window.innerHeight >= document.body.scrollHeight - 500) {
loadMoreContent();
}
}, 200);
window.addEventListener("scroll", throttledScroll);
Результат: Обработчик вызовется максимум 5 раз в секунду (каждые 200ms).
Debouncing: Автосохранение
const textarea = document.querySelector("textarea");
const debouncedSave = debounce(async (content) => {
console.log("Сохраняю...");
const response = await fetch("/api/save", {
method: "POST",
body: JSON.stringify({ content }),
headers: { "Content-Type": "application/json" }
});
console.log("Сохранено!");
}, 2000);
textarea.addEventListener("input", (e) => {
debouncedSave(e.target.value);
});
Результат: Сохраняет только после 2 секунд спокойствия.
Throttling: Обработка resize
const throttledResize = throttle(() => {
const width = window.innerWidth;
const height = window.innerHeight;
console.log(`Новый размер: ${width}x${height}`);
// Пересчитать layout
recalculateLayout();
}, 250);
window.addEventListener("resize", throttledResize);
Результат: Пересчет layout максимум 4 раза в секунду.
Реализация Debounce и Throttle
Простой debounce
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
}
Debounce с immediate опцией
function debounce(func, delay, immediate = false) {
let timeoutId;
return function(...args) {
const callNow = immediate && !timeoutId;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
if (!immediate) {
func(...args);
}
timeoutId = null;
}, delay);
if (callNow) {
func(...args);
}
};
}
// immediate = true выполняет функцию ДО задержки
const debouncedClick = debounce(() => console.log("Click"), 500, true);
button.addEventListener("click", debouncedClick);
// Сразу: "Click"
// Потом не будет вызовов в течение 500ms
Простой throttle
function throttle(func, delay) {
let lastCallTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastCallTime >= delay) {
lastCallTime = now;
func(...args);
}
};
}
Throttle с запуском в конце
function throttle(func, delay) {
let lastCallTime = 0;
let timeoutId;
return function(...args) {
const now = Date.now();
if (now - lastCallTime >= delay) {
lastCallTime = now;
func(...args);
clearTimeout(timeoutId);
} else {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
lastCallTime = Date.now();
func(...args);
}, delay - (now - lastCallTime));
}
};
}
В React
import { useCallback, useRef } from "react";
function useDebounce(callback, delay) {
const timeoutRef = useRef(null);
return useCallback((...args) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => callback(...args), delay);
}, [callback, delay]);
}
function SearchComponent() {
const [query, setQuery] = React.useState("");
const handleSearch = useDebounce(async (value) => {
const results = await fetch(`/api/search?q=${value}`).then(r => r.json());
console.log(results);
}, 300);
return (
<input
value={query}
onChange={(e) => {
setQuery(e.target.value);
handleSearch(e.target.value);
}}
/>
);
}
Когда что использовать
Debouncing:
- Поиск/автозаполнение
- Валидация формы
- Автосохранение
- Отправка данных
Throttling:
- Обработка скролла
- Изменение размера окна
- Движение мыши
- Отслеживание положения
Заключение
Debouncing: Ждёт, пока события закончатся, потом выполняет один раз. Хорошо для событий, которые происходят волнами (печать, изменение значения).
Throttling: Выполняет функцию с фиксированной частотой независимо от количества событий. Хорошо для непрерывных событий (скролл, мышь).
Аналогия:
- Debouncing: Ты жди, пока я закончу писать, потом отправь
- Throttling: Отправляй мне обновления максимум один раз в секунду