> Как реализовать декоратор функции в JavaScript, ограничивающий число вызовов и вызывающий колбэк при последнем вызове (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Яндекс

Стек: JavaScript

> Пример ответа

Короткий ответ

Декоратор onceWithCallback принимает функцию и колбэк, возвращает новую функцию, которая отслеживает количество вызовов через замыкание. При каждом вызове счётчик увеличивается, и после достижения лимита (или при последнем вызове) вызывается колбэк. Реализация использует apply для передачи контекста и аргументов, а также флаг для блокировки дальнейших вызовов.

Подробное объяснение

Основная задача - создать декоратор, который:

  1. Ограничивает количество вызовов целевой функции (например, до N раз).
  2. При каждом вызове выполняет целевую функцию.
  3. При достижении лимита (последний разрешённый вызов) вызывает колбэк, передавая ему результат последнего вызова и контекст.

Ключевые механизмы:

  • Замыкание для хранения счётчика вызовов и флага блокировки.
  • Function.prototype.apply для корректной передачи this и аргументов.
  • Возврат новой функции, которая при каждом вызове проверяет счётчик.

Варианты реализации:

  • С фиксированным лимитом вызовов.
  • С динамическим лимитом, передаваемым как аргумент декоратора.
  • С поддержкой асинхронных функций (через async/await).

Trade-off: при использовании стрелочных функций теряется контекст this, поэтому нужна обычная функция или явная привязка.

На практике

В реальных проектах такой декоратор полезен для:

  • Лимитирования запросов к API (rate limiting).
  • Ограничения обработчиков событий (например, кликов).
  • Логирования последнего вызова в цепочке.

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

JAVASCRIPT
const limitedLog = onceWithCallback(console.log, 3, (result) => {
console.log('Последний вызов, результат:', result);
});

Важно учитывать:

  • Если целевая функция асинхронная, колбэк должен вызываться после разрешения промиса.
  • Для отмены декоратора можно добавить метод cancel.
  • В среде Node.js можно использовать util.deprecate для аналогичных целей.

Пример кода

JAVASCRIPT
function onceWithCallback(fn, limit, callback) {
let callCount = 0;
let isBlocked = false;
let lastResult;
return function(...args) {
if (isBlocked) return;
callCount++;
const context = this;
if (callCount <= limit) {
lastResult = fn.apply(context, args);
}
if (callCount === limit) {
isBlocked = true;
callback.call(context, lastResult);
}
return lastResult;
};
}
// Пример использования
const originalFn = (x) => x * 2;
const decorated = onceWithCallback(originalFn, 3, (result) => {
console.log('Последний результат:', result);
});
console.log(decorated(1)); // 2
console.log(decorated(2)); // 4
console.log(decorated(3)); // 6, затем "Последний результат: 6"
console.log(decorated(4)); // undefined (блокировано)

Как отвечать на собеседовании

  1. Начни с краткого определения задачи и её практической ценности.
  2. Объясни механизм замыкания для хранения состояния.
  3. Покажи базовую реализацию, затем обсуди улучшения (асинхронность, отмена).
  4. Упомяни edge cases: потеря контекста, передача this, работа с null/undefined.
  5. Если спросят про альтернативы - упомяни библиотеки (lodash.once, throttle/debounce).

Что проверяет интервьюер

  • Понимание замыканий и области видимости.
  • Умение работать с контекстом (this) и методами apply/call.
  • Навыки обработки граничных случаев (лимит 0, отрицательный лимит).
  • Способность проектировать гибкие API (колбэк, опции).
  • Знание разницы между синхронными и асинхронными функциями.

Типичные ошибки

  • Использование стрелочной функции для декоратора - теряется this.
  • Неучёт передачи аргументов через arguments или rest-параметры.
  • Вызов колбэка до выполнения целевой функции (если важна последовательность).
  • Отсутствие блокировки после достижения лимита - функция продолжает вызываться.
  • Неправильная обработка undefined результата при лимите 0.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью