Вопрос
Реализуйте класс источника событий, который поддерживает две функции: подписку и отправку. Функция подписки принимает событие и функцию обратного вызова, которая подписывается на событие и возвращает объект подписки. Функция Emit генерирует событие с аргументами, которые будут переданы в обратные вызовы, которые подписываются на это событие. Объект подписки имеет метод Release, который отменяет подписку. В приведенном ниже коде показаны некоторые примеры использования этих функций.
const emitter = new Emitter(); const sub1 = emitter.subscribe('event 1', function(a, b) { console.log(a + b); }) const sub2 = emitter.subscribe('event 2', function(a, b) { console.log(a - b); }) emitter.emit('event 1', 10, 8); // output 18 emitter.emit('event 2', 10, 8); // output 2 sub1.release(); emitter.emit('event 1', 10, 8); // error! sub2.release(); emitter.emit('event 2', 10, 8); // error!
Вопрос начинается с сопоставления одного имени события с одним обратным вызовом. Затем следуют два продолжения:
- Он должен поддерживать разные обратные вызовы, подписывающиеся на одно и то же событие, а выпускная часть все равно должна работать.
- Он должен поддерживать один и тот же обратный вызов, подписавшись на одно и то же событие несколько раз, а выпускная часть должна по-прежнему работать.
В приведенном ниже коде показано несколько примеров того, как эти функции можно использовать с последующими действиями.
const emitter = new Emitter(); const foo = function(a, b) { console.log(a + b); }; const sub1 = emitter.subscribe('event 1', foo) // A different callback subscribes to the same event const sub2 = emitter.subscribe('event 1', function(a, b) { console.log(a * b); }) // Same callback subscribes to the the same event twice const sub3 = emitter.subscribe('event 1', foo) emitter.emit('event 1', 10, 8); // output 18, 80, 18 sub1.release(); emitter.emit('event 1', 10, 8); // output 80, 18 sub2.release(); emitter.emit('event 1', 10, 8); // output 18
Решение
class Emitter { constructor() { this.eventMap = {}; } subscribe(eventName, callback) { if (!this.eventMap[eventName]) { this.eventMap[eventName] = []; } // wrap callback in another function to save as different object // in order to support followup #2 const cb = (...args) => {callback(...args)}; this.eventMap[eventName].push(cb); return { release: () => { const index = this.eventMap[eventName].indexOf(cb); this.eventMap[eventName].splice(index, 1); if (!this.eventMap[eventName].length) { delete this.eventMap[eventName]; } } } } emit(eventName, ...args) { if (!this.eventMap[eventName]) { throw new Error("no such event"); } this.eventMap[eventName].forEach((callback) => { callback.call(undefined, ...args); }); } }
Компания
Мета