В JavaScript часто возникает необходимость контролировать частоту выполнения функций, особенно при обработке событий, таких как прокрутка страницы, изменение размеров окна или ввод текста пользователем. Два ключевых подхода для этого — debounce и throttle. Они позволяют оптимизировать производительность и предотвращают лишние вызовы функций, которые могут нагружать браузер.
Debounce откладывает выполнение функции до тех пор, пока определённое время не пройдёт после последнего вызова. Это особенно полезно для действий, которые должны выполняться только после завершения серии событий.
function debounce(func, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// Использование:
const handleResize = debounce(() => {
console.log('Изменение размера окна завершено');
}, 300);
window.addEventListener('resize', handleResize);
В этом примере функция handleResize будет вызвана только
один раз через 300 мс после того, как пользователь прекратил изменять
размер окна.
func.apply(this, args).Throttle ограничивает частоту вызова функции, выполняя её не чаще определённого интервала времени, независимо от того, сколько раз произошло событие. Это полезно, когда нужно, чтобы функция выполнялась регулярно, но не слишком часто.
function throttle(func, limit) {
let lastCall = 0;
return function (...args) {
const now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
func.apply(this, args);
}
};
}
// Использование:
const handleScroll = throttle(() => {
console.log('Прокрутка страницы');
}, 200);
window.addEventListener('scroll', handleScroll);
В этом примере функция handleScroll будет вызываться не
чаще одного раза каждые 200 мс, даже если пользователь активно скроллит
страницу.
| Параметр | Debounce | Throttle |
|---|---|---|
| Когда выполняется функция | После завершения серии событий | Регулярно с заданным интервалом |
| Цель | Предотвратить лишние вызовы после серии событий | Ограничить частоту вызовов во время активности |
| Примеры | Автосохранение текста, поиск при вводе | Скролл-эффекты, изменение позиции элементов |
Debounce с немедленным вызовом (leading edge):
function debounce(func, delay, immediate = false) {
let timer;
return function (...args) {
const callNow = immediate && !timer;
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
if (!immediate) func.apply(this, args);
}, delay);
if (callNow) func.apply(this, args);
};
}
Такой вариант позволяет выполнить функцию сразу при первом вызове, а последующие игнорировать до окончания интервала.
Throttle с trailing вызовом:
function throttle(func, limit) {
let lastCall = 0;
let timer;
return function (...args) {
const now = Date.now();
const remaining = limit - (now - lastCall);
if (remaining <= 0) {
clearTimeout(timer);
lastCall = now;
func.apply(this, args);
} else if (!timer) {
timer = setTimeout(() => {
lastCall = Date.now();
timer = null;
func.apply(this, args);
}, remaining);
}
};
}
Такой подход позволяет гарантировать выполнение последнего события, которое произошло до окончания интервала.
В библиотеке Popper.js debounce и throttle часто применяются для обновления позиции поппера при изменении окна или скролле:
import { createPopper } from '@popperjs/core';
const button = document.querySelector('#button');
const tooltip = document.querySelector('#tooltip');
const popperInstance = createPopper(button, tooltip, {
placement: 'top',
});
const updatePopper = throttle(() => {
popperInstance.update();
}, 100);
window.addEventListener('resize', updatePopper);
window.addEventListener('scroll', updatePopper);
throttle здесь предотвращает чрезмерные вызовы
update() при быстром скролле.Debounce и throttle — это инструменты контроля частоты выполнения функций, которые помогают снижать нагрузку на браузер и поддерживать плавность работы интерфейсов. Их правильное применение особенно критично в сочетании с динамическими библиотеками вроде Popper.js, где точная и оптимальная позиция элементов зависит от частых событий на странице.