Instantly share code, notes, and snippets.

Embed
What would you like to do?
Revealing constructor pattern event-emitter
// This event emitter emits events, but reserves the right to publish events to
// for its creator. It uses a WeakMap for true encapsulation.
const eesToEventMaps = new WeakMap();
export default class EventEmitter {
constructor(publisher) {
const eventMap = Object.create(null);
eesToEventMaps.set(this, eventMap);
publisher(makePublish(this));
}
on(eventName, handler) {
const eventMap = eesToEventMaps.get(this);
let handlers = eventMap[eventName];
if (!handlers) {
handlers = eventMap[eventName] = [];
}
handlers.push(handler);
}
off(eventName, handler) {
const eventMap = eesToEventMaps.get(this);
const handlers = eventMap[eventName];
if (!handlers) {
return;
}
const index = handlers.indexOf(handler);
if (index === -1) {
return;
}
handlers.splice(index, -1);
}
}
function makePublish(ee) {
const eventMap = eesToEventMaps.get(ee);
return function (eventName, ...args) {
const handlers = eventMap[eventName];
if (handlers) {
handlers.forEach(h => h(...args));
}
};
}
const myEE = new EventEmitter(publish => {
// Wait for interesting things to happen, then call
// `publish("eventName", ...args)`.
});
passToOtherCode(myEE);
// The other code only gets access to `on` and `off`: it cannot trigger spurious
// events, but only listen for them. This makes it safe to pass `myEE` to
// multiple consumers without worrying about them accidentally stepping on each
// other's toes.
// (Of course, they could intentionally step on each other's toes, e.g. by
// overwriting `on` or `off`. For true security you'll need to deeply-freeze
// `myEE`, and do a few more things, e.g. to protect against plan interference
// attacks. We're not focused on that right now, but instead on the API
// ergonomics.)
@trusktr

This comment has been minimized.

trusktr commented Aug 21, 2016

This pattern is awesome!

@trusktr

This comment has been minimized.

trusktr commented Aug 29, 2017

@domenic, please let me know if I'm close: this pattern of the constructor accepting a publish makes this EventEmitter class not ideal for extending, and more ideal for other code to contain references to an EventEmitter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment