Выбор ответа Senior Реализация с нуля обновлено
once ломает emit в EventEmitter
Вопрос
Подписали once('x', A), затем on('x', B) и вызвали emit('x'). Что выведется при первом emit?
class Emitter {
events = {};
on(name, fn) { (this.events[name] ||= []).push(fn); }
off(name, fn) {
const list = this.events[name];
list.splice(list.indexOf(fn), 1);
}
once(name, fn) {
const wrapper = (...a) => { this.off(name, wrapper); fn(...a); };
this.on(name, wrapper);
}
emit(name, ...args) {
(this.events[name] || []).forEach((h) => h(...args));
}
}
Попробуйте сами
Выберите вариант, и откроется разбор.
Показать ответ и разбор
Коротко
Правильный ответ: Только A: B будет пропущен. emit перебирает живой массив.
Варианты ответа
- A и B, в порядке подписки
- Только B: once снимается до вызова
- Только A: B будет пропущенправильный ответ
- Бесконечный цикл из-за
spliceвemit
Разбор
emit перебирает живой массив. Обёртка once во время перебора удаляет себя через splice, B сдвигается на индекс 0, а forEach переходит к индексу 1, где уже ничего нет. B пропускается при этом emit и сработает только при следующем. Исправление: перебирать копию, [...list].forEach(...). Ещё у этого off есть баг: если функции нет в списке, indexOf вернёт -1, и splice(-1, 1) удалит последний обработчик.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.