> Как отследить момент запуска и завершения обычной функции в 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;}};}// Использование с Proxyfunction 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.
> Похожие задачи по frontend
Всегда ли JavaScript выполняется после события onload
Что такое SPA приложение
Можно ли делать асинхронные запросы внутри редюсера Redux и почему
Почему не использовать простой численный цикл в JavaScript
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью