#Что означает ошибка и когда она возникает
MaxListenersExceededWarning — предупреждение EventEmitter о числе подписчиков одного события: по умолчанию больше 10 обработчиков может указывать на утечку. Само предупреждение не останавливает Node.js и не доказывает утечку; проверять нужно, добавляет ли код одинаковые обработчики при каждом запросе, подключении или повторной инициализации.
#Как отличить возможные причины
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Подписка внутри повторяемой функцииКаждый вызов функции делает emitter.on("ready", handler) и оставляет новый слушатель. | После десятого/одиннадцатого вызова появляется предупреждение, количество listeners растёт. | Зарегистрировать обработчик один раз на жизненный цикл объекта. |
| Одноразовую подписку забывают удалятьon() оставляет слушатель после выполнения события; если нужен один вызов, уместен once(). | Одинаковый callback вызывается повторно на следующих событиях. | Использовать once() или точный off(event, handler). |
| Лимит предупреждений просто мал для проектаБолее 10 разных слушателей иногда задуманы архитектурой, и их количество постоянно. | Счётчик не растёт при повторных действиях, назначения различаются. | После проверки архитектуры отдельно рассмотреть документированную настройку лимита. |
#Пошаговая диагностика и устранение
- Зафиксируйте предупреждение, но не считайте его авариейВ терминале проекта запустите приложение обычной командой и скопируйте имя события, тип эмиттера и число слушателей из MaxListenersExceededWarning. Если приложение продолжает работать, это ожидаемое предупреждение, а не обязательное падение. Не отключайте вывод предупреждений.
- Получите стек регистрации обработчиковОткройте встроенный терминал VS Code и запустите отдельный тест командой
node --trace-warnings app.cjsс реальным именем точки входа. При повторении предупреждения стек покажет место добавления обработчиков. Если это production, предварительно воспроизведите в тестовом окружении без пользовательских секретов. - Проверьте, растёт ли число слушателейВ месте, где доступен ваш EventEmitter, временно выведите
emitter.listenerCount("ready")до и после повторного вызова функции. При утечке число растёт, например 1, 2, 3 … 11. Не вызывайте публичные методы с реальными побочными действиями только ради счётчика на рабочем сервере. - Найдите дублирующую регистрацию в исходном кодеВ VS Code нажмите Ctrl+Shift+F, найдите
.on(, особенно обработчик имени события из предупреждения. Сравните, когда выполняется строка: при старте или на каждый запрос, событие подключения и перезагрузку модуля. Часто виноваты регистрация в обработчике HTTP-запроса или повторный start(). - Перенесите постоянную подписку из повторяемой функцииЕсли событие должно обрабатываться на протяжении жизни приложения, регистрируйте on(event,handler) один раз при создании объекта. В учебном примере ниже вызов init() 20 раз не должен приводить к двадцати подпискам: используйте отдельный флаг либо храните жизненный цикл явно.
- Для единичного ожидания используйте once или offКогда обработчик нужен на одно событие, замените
.on()на.once()и убедитесь, что логика не зависит от повторных вызовов. Если подписка живёт ограниченный период, вызовитеemitter.off("ready", handler)с той же ссылкой на функцию, что была передана в on; новая анонимная функция не удалит прежний слушатель. - Не прячьте предупреждение изменением лимита
emitter.setMaxListeners(100)только меняет порог предупреждения, не предотвращает рост массива обработчиков. Допустимо увеличивать лимит лишь если доказано фиксированное и ожидаемое число независимых подписчиков. Сначала устраните повторную регистрацию. - Проверьте после повторной инициализацииНа отдельной тестовой программе выполните 20 одинаковых действий и снова проверьте listenerCount: число должно оставаться ожидаемым, предупреждение не появляется, обработчик вызывается столько раз, сколько предусмотрено. Если перестали приходить события, верните прежнюю регистрацию по Git и пересмотрите жизненный цикл вместо безусловного удаления слушателей.
- Команды для отдельных ситуацийВыберите только подходящий вариант. Это не одна последовательность: часть команд диагностирует проблему, а часть меняет настройки или данные.
#Минимальный пример и проверка на тестовом проекте
const { EventEmitter } = require("node:events");
const emitter = new EventEmitter();
function onReady() { console.log("Готово"); }
let subscribed = false;
function init() {
if (subscribed) return;
emitter.on("ready", onReady);
subscribed = true;
}
for (let i = 0; i < 20; i++) init();
console.log("Слушателей:", emitter.listenerCount("ready"));
emitter.emit("ready");
// Для остановки конкретной подсистемы: emitter.off("ready", onReady);#Проверка результата и действия при неуспехе
После 20 повторных инициализаций число подписок остаётся постоянным, предупреждение не появляется, событие обрабатывается однократно. Если число всё ещё растёт, выясните по стеку --trace-warnings, какой модуль добавляет слушатели; если это библиотека, проверьте её версию и официальные сведения об ошибке.