> Как реализовать функцию-обертку runOnce в JavaScript, вызывающую функцию только один раз? (JavaScript)

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

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

Стек: JavaScript

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

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

Функция-обертка runOnce в JavaScript вызывается только один раз, после чего последующие вызовы игнорируются или возвращают кешированный результат. Реализуется через замыкание с флагом called и сохранением результата первого вызова. Для сложных сценариев используется WeakMap для привязки к объектам. Типичная реализация - once из lodash.

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

runOnce - это higher-order function, которая принимает функцию fn и возвращает новую функцию с модифицированным поведением: при первом вызове выполняется fn и запоминается её результат, при последующих вызовах fn не вызывается, а возвращается сохранённый результат. Это реализуется через замыкание, где хранятся флаги called и result.

Базовый механизм: при каждом вызове обёртки проверяется called. Если false, вызывается fn, результат сохраняется в result, called устанавливается в true. Если true, возвращается result. Важно обрабатывать контекст (this) и аргументы через apply или call.

Для методов объектов используется WeakMap для хранения состояния на экземпляр, чтобы избежать утечек памяти. В современных фреймворках (React, Vue) runOnce применяется для однократной инициализации, подписок или загрузки данных.

На практике

В продакшене обычно используют готовые реализации из lodash/underscore или пишут кастомную с учётом:

  • корректной передачи this и аргументов
  • обработки ошибок (если fn выбросила исключение при первом вызове, флаг called не должен устанавливаться)
  • поддержки асинхронных функций (возврат промиса)
  • опционального сброса состояния для тестирования

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

  • однократная инициализация WebSocket соединения
  • загрузка конфигурации при старте приложения
  • debounce с гарантией единственного вызова
  • подписка на события, которая должна сработать только раз

Пример кода

JAVASCRIPT
// Базовая реализация
function runOnce(fn) {
let called = false;
let result;
return function(...args) {
if (!called) {
called = true;
result = fn.apply(this, args);
}
return result;
};
}
// С обработкой ошибок
function runOnceSafe(fn) {
let called = false;
let result;
return function(...args) {
if (!called) {
called = true;
try {
result = fn.apply(this, args);
} catch (error) {
called = false; // сброс флага при ошибке
throw error;
}
}
return result;
};
}
// Для методов объектов с WeakMap
const onceMap = new WeakMap();
function runOnceMethod(fn) {
return function(...args) {
if (!onceMap.has(this)) {
onceMap.set(this, { called: false, result: undefined });
}
const state = onceMap.get(this);
if (!state.called) {
state.called = true;
state.result = fn.apply(this, args);
}
return state.result;
};
}

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

Начни с базового определения и простой реализации через замыкание. Покажи понимание передачи this и аргументов. Затем обсуди edge cases: ошибки, асинхронность, утечки памяти. Упомяни WeakMap для методов объектов. Сравни с готовыми решениями из lodash. Если спросят про тестирование - скажи про сброс состояния через reset или создание новой обёртки.

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

  • понимание замыканий и области видимости
  • умение работать с this и контекстом вызова
  • знание паттернов higher-order functions
  • внимание к edge cases (ошибки, асинхронность, мультиинстансность)
  • способность проектировать API (опции, reset, совместимость)
  • понимание memory management (WeakMap, утечки)

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

  • потеря контекста this при вызове fn (использование стрелочной функции в обёртке)
  • отсутствие обработки ошибок - флаг called устанавливается даже при исключении
  • игнорирование аргументов - передача только первого аргумента вместо всех через rest/spread
  • мутация внешнего состояния без необходимости
  • использование глобальных переменных вместо замыкания
  • неправильная работа с асинхронными функциями (не возвращается промис)

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

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