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

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

Компании: Домклик

Стек: JavaScript

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

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

Для отслеживания момента запуска и завершения обычной функции в JavaScript можно использовать обёртку (wrapper), которая логирует вызов до и после выполнения оригинальной функции. Другой вариант - использовать Proxy с перехватчиком apply, что позволяет добавлять логирование без изменения исходного кода. В асинхронных функциях дополнительно обрабатывают Promise, чтобы корректно фиксировать завершение.

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

Отслеживание запуска и завершения функции требуется для профилирования, дебаггинга или мониторинга производительности. В JavaScript нет встроенного механизма хуков для обычных функций, поэтому применяются паттерны обёртки.

Первый подход - явная обёртка: создаётся новая функция, которая вызывает оригинал, оборачивая его в логирование. Это просто, но требует изменения кода в месте вызова.

Второй подход - использование Proxy с handler apply. Это позволяет перехватывать вызовы любой функции, не изменяя её саму. Proxy подходит для случаев, когда функция передаётся как колбэк или хранится в объекте.

Для асинхронных функций (async/await или возвращающих Promise) необходимо дождаться разрешения Promise, чтобы зафиксировать завершение. Иначе лог завершения появится сразу после вызова, а не после реального окончания работы.

Также можно использовать декораторы (в TypeScript или с Babel), но в чистом JavaScript они не поддерживаются нативно.

На практике

Наиболее практичный способ для production - написать универсальную функцию-декоратор, которая принимает любую функцию и возвращает обёртку с логированием. Для синхронных функций достаточно вызвать оригинал и залогировать результат. Для асинхронных - обернуть вызов в Promise.resolve и добавить .then() или await.

Пример использования: профилирование времени выполнения функции в dev-среде, логирование вызовов API для отладки, трекинг пользовательских действий.

Важно учитывать, что обёртка создаёт дополнительный вызов, что может повлиять на производительность в горячих путях. Для production-мониторинга лучше использовать специализированные инструменты (например, Performance API).

Пример кода

JAVASCRIPT
// Универсальная функция-декоратор для отслеживания
function trace(fn) {
return function(...args) {
console.log(`[TRACE] запуск: ${fn.name || 'anonymous'}`);
const start = performance.now();
try {
const result = fn.apply(this, args);
// Если результат - Promise, ждём его
if (result && typeof result.then === 'function') {
return result.then(
(value) => {
console.log(`[TRACE] завершено (async): ${fn.name}, время: ${performance.now() - start}ms`);
return value;
},
(error) => {
console.error(`[TRACE] ошибка (async): ${fn.name}`, error);
throw error;
}
);
}
console.log(`[TRACE] завершено: ${fn.name}, время: ${performance.now() - start}ms`);
return result;
} catch (error) {
console.error(`[TRACE] ошибка: ${fn.name}`, error);
throw error;
}
};
}
// Использование с Proxy
function createTracedProxy(fn) {
return new Proxy(fn, {
apply(target, thisArg, args) {
console.log(`[PROXY] запуск: ${target.name}`);
const start = performance.now();
const result = Reflect.apply(target, thisArg, args);
console.log(`[PROXY] завершено: ${target.name}, время: ${performance.now() - start}ms`);
return result;
}
});
}
// Пример
function fetchData(url) {
return fetch(url).then(res => res.json());
}
const tracedFetch = trace(fetchData);
tracedFetch('/api/data');

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

Начни с краткого описания задачи - отслеживание вызовов функции для дебаггинга или профилирования. Затем перечисли основные подходы: обёртка, Proxy, декораторы. Объясни разницу между синхронными и асинхронными функциями - это ключевой момент для senior. Упомяни trade-off: обёртка проще, но меняет код; Proxy элегантнее, но может быть медленнее. Приведи пример с обработкой Promise. Заверши практическим советом: для production используй Performance API или библиотеки типа pino для логирования.

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

  • Понимание замыканий и контекста this в обёртках.
  • Знание асинхронности: как корректно обработать Promise в декораторе.
  • Умение работать с Proxy и Reflect.
  • Осознание производительности: обёртка создаёт overhead.
  • Способность предложить решение для разных сценариев (синхронные, асинхронные, методы классов).

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

  • Не учитывают, что функция может быть асинхронной - лог завершения появляется до реального окончания.
  • Забывают про this в обёртке - используют стрелочную функцию вместо обычной.
  • Пытаются модифицировать прототип Function, что глобально меняет поведение всех функций.
  • Используют console.time без обработки ошибок - при исключении время не выводится.
  • Не обрабатывают случай, когда функция возвращает thenable, не являющийся Promise.

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

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