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

Реализовать функцию debounce

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

Условие

Напишите функцию debounce(fn, delay), которая откладывает вызов переданной функции fn до тех пор, пока не пройдёт delay миллисекунд с момента последнего вызова.

Требования

  1. Функция должна принимать два аргумента:

    • fn — функция, вызов которой нужно отложить
    • delay — задержка в миллисекундах
  2. Возвращаемая функция должна:

    • Сбрасывать таймер при каждом новом вызове
    • Вызывать оригинальную функцию только после истечения задержки
    • Корректно передавать аргументы в оригинальную функцию

Пример использования

const debouncedLog = debounce((message) => console.log(message), 300);

debouncedLog("Привет"); // Не выведется
debouncedLog("Мир");   // Через 300мс выведет "Мир"

Бонус

Добавьте метод cancel() для отмены отложенного вызова.

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

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

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

Решение функции debounce

Что такое debounce?

Debounce — это техника оптимизации, которая откладывает выполнение функции до тех пор, пока не пройдёт определённое время с момента последнего вызова. Это критически важно при обработке часто вызываемых событий (изменение input, скролл, resize), чтобы избежать множественных ненужных вычислений.

Реализация базовой версии

function debounce(fn, delay) {
  let timeoutId;
  
  return function debounced(...args) {
    // Очищаем предыдущий таймер
    clearTimeout(timeoutId);
    
    // Устанавливаем новый таймер
    timeoutId = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

С методом cancel() и улучшениями

function debounce(fn, delay) {
  let timeoutId;
  
  const debounced = function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
  
  // Метод отмены
  debounced.cancel = () => {
    clearTimeout(timeoutId);
  };
  
  // Метод немедленного вызова
  debounced.flush = () => {
    clearTimeout(timeoutId);
    fn.apply(this);
  };
  
  return debounced;
}

TypeScript версия

function debounce<T extends (...args: any[]) => any>(
  fn: T,
  delay: number
): ((...args: Parameters<T>) => void) & { cancel: () => void; flush: () => void } {
  let timeoutId: ReturnType<typeof setTimeout> | null = null;
  
  const debounced = function(this: ThisParameterType<T>, ...args: Parameters<T>) {
    if (timeoutId !== null) {
      clearTimeout(timeoutId);
    }
    
    timeoutId = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
  
  debounced.cancel = () => {
    if (timeoutId !== null) {
      clearTimeout(timeoutId);
      timeoutId = null;
    }
  };
  
  debounced.flush = () => {
    if (timeoutId !== null) {
      clearTimeout(timeoutId);
      fn.apply(this);
    }
  };
  
  return debounced as any;
}

Примеры использования

// Поиск при вводе
const search = debounce((query) => {
  console.log("Ищу:", query);
}, 300);

input.addEventListener("input", (e) => search(e.target.value));

// Обработка resize
const handleResize = debounce(() => {
  console.log("Размер изменился");
}, 500);

window.addEventListener("resize", handleResize);

// Отмена
const submit = debounce(() => {
  console.log("Отправляю форму");
}, 1000);

submit();
submit.cancel(); // Вызов не произойдёт

Ключевые моменты

  • Контекст (this): используем .apply(this, args) для сохранения контекста вызова
  • Аргументы: используем rest-параметры (...args) для захвата всех аргументов
  • Сброс таймера: clearTimeout() отменяет предыдущий вызов перед установкой нового
  • Замыкание: timeoutId сохраняется в замыкании для сохранения состояния между вызовами
  • cancel(): даёт возможность отменить запланированный вызов
  • flush(): позволяет немедленно выполнить функцию, очистив таймер

Различие между debounce и throttle

Частая ошибка — путать debounce с throttle. Debounce выполняет функцию один раз после периода спокойствия, а throttle выполняет функцию максимум один раз за интервал времени. Выбор зависит от сценария: debounce — для поиска/автосохранения, throttle — для скролла/ресайза.