Выбор ответа Senior Модули и npm обновлено

Циклический require в CommonJS

Вопрос

a.js делает const b = require("./b"), а b.js в начале делает const a = require("./a") и в своей функции вызывает a.helper(). a.js экспортирует module.exports = { helper } в конце файла. Что произойдёт при запуске node a.js и вызове функции из b во время загрузки?

Попробуйте сами

Выберите вариант, и откроется разбор.

Показать ответ и разбор

Коротко

Правильный ответ: b получит пустой module.exports из a, helper undefined. CommonJS кеширует модуль в require.cache до выполнения его кода.

Варианты ответа

  1. Node обнаружит цикл и бросит ошибку ERR_REQUIRE_CYCLE при загрузке
  2. Node загрузит a повторно с начала, и всё сработает
  3. Будет бесконечная рекурсия загрузки и переполнение стека
  4. b получит пустой module.exports из a, helper undefinedправильный ответ

Разбор

CommonJS кеширует модуль в require.cache до выполнения его кода. Когда b требует a, который ещё выполняется, он получает текущий module.exports модуля a: пустой объект, потому что присваивание стоит в конце файла. Ошибки нет, рекурсии нет, повторной загрузки нет, просто TypeError: a.helper is not a function в момент вызова.

Лечение: разорвать цикл (вынести общее в третий модуль), обращаться к зависимости лениво внутри функций, а не на верхнем уровне, или не переприсваивать module.exports, а дописывать свойства в exports. В ESM циклы обрабатываются через живые привязки, но там своя ловушка с обращением до инициализации (TDZ).

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.

Термины по теме