Как реализовать хранение нескольких колбэков для одного события в EventEmitter
Что проверяют
Умеете ли вы выбрать структуру данных для подписок и продумать поведение на краях: удаление во время рассылки, однократные подписки, ошибки в обработчиках. Следом обычно просят дописать once или спрашивают, как избежать утечек памяти.
Как отвечать
Хранилище: Map, где ключ это имя события, а значение массив или Set обработчиков. Map лучше обычного объекта: у {} есть прототип, и событие с именем constructor найдёт в нём функцию Object вместо массива. Если всё же объект, то Object.create(null).
Дальше выбор между массивом и Set:
- массив: сохраняет порядок и допускает повторную подписку одной функции. Так устроен
EventEmitterв Node.js; Set: тоже хранит порядок вставки, но одну и ту же функцию добавит один раз. Похоже наaddEventListener, который игнорирует дубликат того же обработчика с тем жеcapture.
Методы и их детали:
on: добавляет обработчик и возвращает функцию отписки, так удобнее вuseEffectи компонентах;emit: перебирает копию списка,[...listeners]. Тогда отписка или новая подписка внутри обработчика не сломает текущий обход;off: удаляет по ссылке, опустевший список убирает изMapцеликом;once: оборачивает функцию, снимает обёртку перед вызовом и запоминает оригинал в свойстве обёртки, чтобыoff(event, original)тоже её нашёл.
Про ошибки: в Node.js исключение в обработчике прерывает emit, и остальные подписчики не вызываются. Если нужно другое поведение, оборачивайте каждый вызов в try/catch и сообщайте об ошибке отдельно.
Где спотыкаются
Мутируют массив во время обхода: splice внутри forEach при отписке из обработчика приводит к пропуску соседнего подписчика.
Реализуют once так, что его нельзя снять через off по исходной функции, потому что в списке лежит обёртка.
Забывают про утечки. Компонент подписался и не отписался при размонтировании, обработчики копятся. Node.js поэтому предупреждает, когда на одно событие подписано больше десяти слушателей, лимит меняется через setMaxListeners.