← Назад к вопросам

В чем разница между Throttling и Debouncing?

2.0 Middle🔥 211 комментариев
#JavaScript Core#Оптимизация и производительность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

В чем разница между 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 миллисекунд)
  • Гарантированное выполнение с постоянной частотой
  • Идеален для скролла, изменения размера окна, движения мыши
  • Лучше реактивность

Прямое сравнение

ПараметрDebouncingThrottling
ВыполнениеПосле окончания событийПериодически
ЧастотаОдин разНесколько раз на интервал
ЗадержкаМожет быть задержкаПредсказуемая частота
Луч.сценарийПоиск, автосохранениеСкролл, 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: Отправляй мне обновления максимум один раз в секунду
В чем разница между Throttling и Debouncing? | PrepBro