ПроКодинг - Откроем для вас мир IT!

Вы когда-нибудь замечали, как сайт начинает тормозить, когда вы быстро печатаете в поиске или прокручиваете страницу? Это не баг - это просто JavaScript, который перегружен событиями. Каждое нажатие клавиши, каждый сдвиг мыши, каждый скролл - это вызов функции. Если их обрабатывать все подряд, браузер сходит с ума. Вот где приходят на помощь троттлинг и дебаунс. Эти два инструмента не просто улучшают производительность - они делают интерфейсы живыми, быстрыми и предсказуемыми.

Что такое дебаунс и зачем он нужен

Дебаунс работает по принципу: «Подожди, пока человек перестанет делать что-то, и только потом сделай работу». Представьте, что вы пишете поиск. Пользователь вводит «квартира в Казани». Он не ждёт, пока он наберёт всё - он печатает по буквам. Без дебаунса вы отправите 11 запросов к серверу: на «к», на «кв», на «квa», и так далее. Это лишняя нагрузка, лишние деньги на хостинг, лишнее время ожидания для пользователя.

Дебаунс берёт все эти вызовы и объединяет их в один. Он ставит таймер на 400 миллисекунд. Каждый раз, когда пользователь нажимает клавишу, таймер сбрасывается. Только когда он перестал печатать - и прошло 400 мс - тогда и отправляется запрос. Всё, что происходило до этого, игнорируется. Результат? Один запрос вместо десятка.

Это идеально для:

  • Поисковых полей
  • Автосохранения форм
  • Валидации данных
  • Обновления настроек в реальном времени

Реализация простая. Создаёте переменную для хранения таймера. При каждом вызове функции - очищаете старый таймер, ставите новый. Если таймер не сработал - функция не выполняется. Вот как это выглядит в коде:

function debounce(func, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

const searchHandler = debounce(fetchResults, 400);
input.addEventListener('input', searchHandler);

Здесь fetchResults - ваша настоящая функция, которая делает запрос. А searchHandler - обёртка, которая ждёт, пока пользователь перестанет печатать. И да, вы можете настроить, чтобы функция сработала сразу в начале (leading), а не только в конце (trailing). Но по умолчанию - только в конце.

Что такое троттлинг и когда его использовать

Троттлинг - это противоположность дебаунсу. Он не ждёт, пока событие закончится. Он говорит: «Слишком много вызовов? Хорошо. Я буду выполнять твою функцию только раз в 100 мс. Остальное - игнорирую».

Представьте, что вы отслеживаете скролл. Пользователь прокручивает страницу - браузер генерирует сотни событий в секунду. Если вы обновляете индикатор прогресса при каждом событии - вы заставляете браузер перерисовывать элемент 100 раз в секунду. Это жестоко. Троттлинг решает это: он даёт вам ровно 10 обновлений в секунду. Ровно. Регулярно. Без перегрузки.

Это идеально для:

  • Скролла (progress bar, параллакс)
  • Изменения размера окна (resize)
  • Движения мыши (mousemove)
  • Drag-and-drop с отслеживанием позиции

Как это работает? У вас есть переменная, которая хранит время последнего выполнения. При каждом вызове вы проверяете: прошло ли уже 100 мс? Если да - выполняете функцию и обновляете время. Если нет - просто ждёте. Можно также добавить «leading» (выполнить сразу при первом вызове) и «trailing» (выполнить, если событие закончилось, но не успело сработать).

function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

const updateProgress = throttle(() => {
  const scrollPercent = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;
  progressBar.style.width = scrollPercent + '%';
}, 100);

window.addEventListener('scroll', updateProgress, { passive: true });

Обратите внимание на { passive: true }. Это не просто опция - это важный трюк. Он говорит браузеру: «Этот обработчик не будет вызывать preventDefault()». Браузер может оптимизировать скролл, не дожидаясь JS. Без этого - скролл будет лагать даже с троттлингом.

Индикатор прогресса при прокрутке обновляется регулярно каждые 100 мс, игнорируя избыточные события.

Разница между дебаунсом и троттлингом - в сути

Многие путают эти два метода. Но разница простая:

Сравнение дебаунса и троттлинга
Параметр Дебаунс Троттлинг
Цель Выполнить один раз после паузы Выполнять с фиксированным интервалом
Когда срабатывает После окончания серии событий Первый раз сразу, потом через интервал
Пример Поиск: запрос только после окончания ввода Скролл: обновление прогресса каждые 100 мс
Если событие не прекращается Функция никогда не сработает Функция сработает через каждый интервал

Дебаунс - это «ждём конца». Троттлинг - это «не перегружайся». Выбор между ними зависит от того, что вам важнее: точность результата или стабильность обновлений.

Почему стоит использовать Lodash

Вы можете написать свои версии дебаунса и троттлинга. Это полезно для обучения. Но в продакшене? Лучше использовать Lodash. Его функции _.debounce() и _.throttle() протестированы миллионами разработчиков. Они учитывают edge-кейсы: отмену, принудительный запуск, правильную работу с this, обработку аргументов, работу в строгом режиме. Вы не сможете написать лучше. И вы не захотите.

import { debounce, throttle } from 'lodash';

const search = debounce(fetchAPI, 400);
const scrollHandler = throttle(updateProgress, 100);

Lodash - это не «ещё одна библиотека». Это стандарт де-факто для таких задач. Если вы работаете в команде - используйте его. Это сэкономит вам часы отладки.

Сравнение дебаунса и троттлинга: фильтрация множества событий в два упорядоченных потока.

Ошибки, которые делают новички

Вот что часто идёт не так:

  • Слишком маленький интервал. Дебаунс на 50 мс - это почти без него. Троттлинг на 20 мс - вы снова перегружаете браузер.
  • Слишком большой интервал. Дебаунс на 2 секунды? Пользователь подумает, что система зависла. Троттлинг на 1 секунду? Прогресс-бар будет прыгать, как булавка.
  • Игнорирование passive: true. Без этого скролл будет тормозить даже с троттлингом.
  • Использование дебаунса там, где нужен троттлинг. Например, для отслеживания мыши. Вы будете ждать, пока пользователь перестанет двигать мышь - а он её не перестаёт. Результат - никаких обновлений.
  • Забыть про отмену. Если пользователь закрыл окно, а таймер всё ещё висит - вы тратите память. Добавьте cancel() или clearTimeout() при уничтожении компонента.

Правило простое: начните с 300-400 мс для дебаунса и 100-150 мс для троттлинга. Тестируйте. Смотрите, как ведёт себя интерфейс. Не гонитесь за идеалом - гонитесь за плавностью.

Когда не нужно использовать дебаунс и троттлинг

Эти методы - не волшебная таблетка. Есть сценарии, где они не помогут, а только навредят:

  • Кнопки. Нажали - выполнили. Нет смысла ждать.
  • Формы с отправкой. Если пользователь нажал «Отправить» - не ждите. Выполняйте сразу.
  • Анимации с requestAnimationFrame. Для анимаций есть свой механизм. Троттлинг тут не нужен.
  • Реактивные системы. В React, Vue, Svelte часто используются хуки и реактивность. Дебаунс там не всегда нужен - он может нарушить логику.

Не используйте эти техники ради них самих. Используйте, когда они решают реальную проблему: слишком много вызовов, тормозит интерфейс, растёт нагрузка на сервер.

Чем отличается дебаунс от троттлинга?

Дебаунс ждёт, пока пользователь перестанет взаимодействовать, и выполняет функцию один раз в конце. Троттлинг выполняет функцию с фиксированным интервалом - например, каждые 100 мс, независимо от того, сколько раз событие произошло. Дебаунс подходит для поиска, троттлинг - для скролла.

Какой интервал выбрать для дебаунса?

Для поиска - 300-500 мс. Для автосохранения - 1000-2000 мс. Меньше - будет лишний трафик. Больше - пользователь подумает, что система не отвечает. Начните с 400 мс и тестируйте на реальных пользователях.

Можно ли использовать оба метода вместе?

Да, и это часто делают. Например, при скролле вы можете использовать троттлинг для обновления индикатора, а дебаунс - для сохранения позиции скролла, когда пользователь перестал прокручивать. Разные задачи - разные инструменты.

Почему троттлинг работает лучше при скролле, чем дебаунс?

Потому что при скролле пользователь не «перестаёт» - он постоянно двигает колесико. Дебаунс в этом случае никогда не сработает. Троттлинг же гарантирует, что обновление будет происходить регулярно - например, каждые 100 мс - даже если событий 500 за секунду.

Нужно ли писать свои реализации?

Только если вы учитесь. В реальных проектах используйте Lodash или аналоги. Они учитывают краевые случаи: отмену, контекст this, работу с аргументами, производительность. Ваша версия может работать, но не будет такой надёжной.

Если вы ещё не применяете троттлинг и дебаунс - начните с одного события. Возьмите поиск на сайте, оберните его в дебаунс с задержкой 400 мс. Запустите. Посмотрите, как уменьшилась нагрузка на сервер. Это не теория - это реальная экономия ресурсов. И плавный, быстрый интерфейс - это не фича. Это ожидание пользователя.