Debounce и throttle

↑ 2.1 JavaScript: продвинутый · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Классическая задача на замыкания и таймеры; реализуют «на доске».

Объяснение

DebounceThrottle
Идеявызвать после паузы в событияхне чаще одного раза за интервал
Примерпоиск при вводе, автосохранениескролл, resize, mousemove
Вызовtrailing (после паузы)leading/trailing по периодам
function debounce(fn, delay, { leading = false } = {}) {
  let timer;
  return function debounced(...args) {
    const callNow = leading && !timer;
    clearTimeout(timer);
    timer = setTimeout(() => { timer = null; if (!leading) fn.apply(this, args); }, delay);
    if (callNow) fn.apply(this, args);
  };
}
 
function throttle(fn, interval) {
  let last = 0, timer, lastArgs;
  return function throttled(...args) {
    const now = Date.now(), remaining = interval - (now - last);
    lastArgs = args;
    if (remaining <= 0) { clearTimeout(timer); timer = null; last = now; fn.apply(this, args); }
    else if (!timer) timer = setTimeout(() => { last = Date.now(); timer = null; fn.apply(this, lastArgs); }, remaining);
  };
}
input.addEventListener("input", debounce((e) => search(e.target.value), 300));
window.addEventListener("scroll", throttle(updateHeader, 100), { passive: true });

Во Vue: watchDebounced, useDebounceFn, useThrottleFn (VueUse); для запросов — отмена предыдущих (AbortController), в Vue Query — keepPreviousData.

Альтернативы: requestAnimationFrame для визуальных обновлений, IntersectionObserver вместо scroll-слушателей.

Нюансы и подводные камни

  • Debounce на общем экземпляре создаётся один раз (не внутри функции рендера) — иначе таймеры разные.
  • Отмена при размонтировании компонента: debounced.cancel().
  • Устаревшие ответы: медленный запрос может прийти после нового — используйте отмену или сопоставление токенов.
  • Слишком большая задержка портит UX.

Практика

  1. Реализуйте debounce с cancel и flush, throttle с leading/trailing.
  2. Реализуйте поиск с debounce и отменой предыдущего запроса.
  3. Замените scroll-слушатель на IntersectionObserver.

Вопросы с ответами

Связанные темы