Загрузка...
Загрузка...
Продолжая работу с платформой, вы принимаете условия Политики конфиденциальности и использование файлов cookie.
Debounce и Throttle — это техники оптимизации производительности, которые контролируют частоту выполнения функций, особенно при обработке событий, которые могут вызываться очень часто (scroll, resize, input и т.д.).
Debounce откладывает выполнение функции до тех пор, пока не пройдёт определённое время с момента последнего вызова.
function debounce(func, delay) {
let timeoutId;
return function(...args) {
// Очищаем предыдущий таймер
clearTimeout(timeoutId);
// Устанавливаем новый таймер
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// Использование
const searchInput = document.querySelector('#search');
const handleSearch = debounce((event) => {
console.log('Поиск:', event.target.value);
// API запрос
}, 500);
searchInput.addEventListener('input', handleSearch);
delay миллисекундРезультат:
Если пользователь печатает "javascript", вместо 10 запросов (по одному на каждую букву) отправится всего один запрос через 500мс после того, как пользователь закончит печатать.
Throttle гарантирует, что функция будет выполняться не чаще, чем раз в указанный интервал времени.
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
}
// Использование
const handleScroll = throttle(() => {
console.log('Scroll position:', window.scrollY);
// Проверка достижения конца страницы
}, 1000);
window.addEventListener('scroll', handleScroll);
inThrottle = truelimit миллисекундРезультат:
Если пользователь быстро скроллит страницу, функция будет вызываться максимум раз в секунду, вместо сотен раз в секунду.
| Характеристика | Debounce | Throttle |
|---|---|---|
| Поведение | Выполняется после паузы | Выполняется через интервалы |
| Первый вызов | Откладывается | Выполняется сразу |
| Частые вызовы | Все сбрасываются | Выполняется раз в N мс |
| Использование | Поиск, автосохранение | Scroll, resize, tracking |
Представим, что событие происходит 10 раз за секунду:
Без оптимизации:
█ █ █ █ █ █ █ █ █ █ (10 вызовов)
С Debounce (500ms):
░ ░ ░ ░ ░ ░ ░ ░ ░ █ (1 вызов после паузы)
С Throttle (500ms):
█ ░ ░ ░ ░ █ ░ ░ ░ ░ (2 вызова с интервалами)
function debounce(func, delay, { leading = false, trailing = true } = {}) {
let timeoutId;
return function(...args) {
const isInvokingLater = !timeoutId;
clearTimeout(timeoutId);
if (leading && isInvokingLater) {
func.apply(this, args);
}
timeoutId = setTimeout(() => {
if (trailing) {
func.apply(this, args);
}
timeoutId = null;
}, delay);
};
}
leading: true — вызов в началеtrailing: true — вызов в конце (по умолчанию)function throttle(func, limit, { leading = true, trailing = true } = {}) {
let inThrottle;
let lastArgs;
return function(...args) {
if (!inThrottle) {
if (leading) {
func.apply(this, args);
}
inThrottle = true;
setTimeout(() => {
inThrottle = false;
if (trailing && lastArgs) {
func.apply(this, lastArgs);
lastArgs = null;
}
}, limit);
} else {
lastArgs = args;
}
};
}
В продакшене часто используют готовые решения:
import { debounce, throttle } from 'lodash';
const debouncedSearch = debounce(searchFunction, 500);
const throttledScroll = throttle(scrollHandler, 1000);
const debouncedFn = debounce(someFunction, 1000);
// Отменить ожидающий вызов
debouncedFn.cancel();
import { useCallback, useRef, useEffect } from 'react';
function useDebounce(callback, delay) {
const timeoutRef = useRef(null);
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
return useCallback((...args) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
callback(...args);
}, delay);
}, [callback, delay]);
}
// Использование в компоненте
function SearchComponent() {
const handleSearch = useDebounce((value) => {
console.log('Searching:', value);
}, 500);
return (
<input
type="text"
onChange={(e) => handleSearch(e.target.value)}
/>
);
}
this// Неправильно
function debounce(func, delay) {
let timeoutId;
return function() {
clearTimeout(timeoutId);
timeoutId = setTimeout(func, delay); // теряем this
};
}
// Правильно
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay); // ✅
};
}
// Неправильно в React
function Component() {
// Каждый рендер создаёт новую функцию
const handler = debounce(() => {}, 500);
return <input onChange={handler} />;
}
// Правильно
function Component() {
// Функция создаётся один раз
const handler = useMemo(
() => debounce(() => {}, 500),
[]
);
return <input onChange={handler} />;
}
this) и аргументы при реализацииНа собеседовании:
Часто просят реализовать debounce/throttle с нуля и объяснить различия. Важно понимать не только реализацию, но и практические кейсы применения.