Skip to content

Instantly share code, notes, and snippets.

@aronanda
Last active June 30, 2020 05:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aronanda/18b6397c355da88ca170d01e0cc02628 to your computer and use it in GitHub Desktop.
Save aronanda/18b6397c355da88ca170d01e0cc02628 to your computer and use it in GitHub Desktop.
Event Emitter
function assign(source, target) {
const fields = Object.getOwnPropertyNames(source.constructor.prototype).filter(f => f !== 'constructor')
for (const key of fields) {
if (key.substr(0, 1) === '_')
continue
const info = Object.getOwnPropertyDescriptor(source.constructor.prototype, key)
if (info.value) {
Object.defineProperty(target, key, { value: source[key].bind(source), configurable: true, writable: true })
} else {
const field = { configurable: true }
if (info.get)
field.get = info.get.bind(source)
if (info.set)
field.set = info.set.bind(source)
if (!_.isEmpty(field))
Object.defineProperty(target, key, field)
}
}
}
class PrivateEvent {
constructor(event, opts = {}) {
assign(this, event)
Object.defineProperty(this, 'ctx', { value: opts.ctx })
Object.defineProperty(this, '_events', { value: new Map })
}
on(topic, handler) {
if (!this._events.has(topic))
this._events.set(topic, new Set)
if (typeof handler === 'undefined')
return this._events.get(topic)
if (typeof this.ctx === 'object')
handler = handler.bind(this.ctx)
this._events.get(topic).add(handler)
return this.off.bind(this, topic, handler)
}
off(topic, handler) {
if (!this._events.has(topic))
return
const set = this._events.get(topic)
if (typeof handler === 'undefined')
return this._events.delete(topic)
if (set.has(handler))
set.delete(handler)
}
once(topic, handler) {
if (typeof this.ctx === 'object')
handler = handler.bind(this.ctx)
const _handler = (...args) => {
handler(...args)
this.off(topic, _handler)
}
this.on(topic, _handler)
}
emit(topic, ...payload) {
if (!this._events.has(topic))
return
const set = this._events.get(topic)
for (const fn of set)
fn(...payload)
}
has(topic) {
return this.size(topic) > 0
}
get length() {
return this.size()
}
size(topic) {
if (typeof topic === 'undefined')
return this._events.size
if (!this._events.has(topic))
throw new Error(`topic "${topic}" not found`)
return this._events.get(topic).size
}
clear() {
for (const [ topic ] of this._events)
this.off(topic)
}
get _events() {
return this._events
}
}
class Event {}
Object.defineProperty(Event, 'Private', { value: PrivateEvent })
module.exports = new Proxy(Event, {
construct (target, args, ctx) {
const event = Reflect.construct(target, args, ctx)
Reflect.construct(PrivateEvent, [event, ...args])
return event
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment