All questions

Event Emitter II

Premium

Event Emitter II

This is the same pub/sub primitive as Event Emitter, with one change to the API. Instead of on/off, there is a single subscribe(eventName, callback) that returns a handle — a small object with a release() method. Calling release() removes that one subscription. There is no off(eventName, callback). The handle is the only way to unsubscribe.

The handle pattern shows up in RxJS, in DOM AbortController, and in most modern bus libraries because it sidesteps a real footgun in the on/off design: if the caller passes an inline arrow function to on, they no longer hold a reference equal to it and can never call off for it. A handle is unique by construction.

Signature

class EventEmitter {
  subscribe(eventName, callback)
  // returns { release: () => void }

  emit(eventName, ...args)
  // calls each live subscriber for eventName, in subscription order, with args
}

eventName is a string. callback is a function. release() returns nothing.

Examples

Basic subscribe + emit:

const bus = new EventEmitter();
const sub = bus.subscribe('login', (user) => console.log('hi', user));
bus.emit('login', 'ada');     // logs: hi ada
sub.release();
bus.emit('login', 'ada');     // no listeners — silent

Release one of several subscribers:

const bus = new EventEmitter();
const a = bus.subscribe('tick', () => console.log('a'));
const b = bus.subscribe('tick', () => console.log('b'));
const c = bus.subscribe('tick', () => console.log('c'));
b.release();
bus.emit('tick');             // logs: a   then   c

Double release is a safe no-op:

const sub = bus.subscribe('x', () => {});
sub.release();
sub.release();                // does NOT throw, does NOT remove anything else

Notes

  • Subscription order matters — listeners fire in the order they were subscribed, first-in first-fired.
  • Same callback, two handles — calling subscribe with the same function twice creates two independent handles; releasing one leaves the other firing.
  • No listeners is fineemit on an event with no subscribers is a silent no-op, not an error.
  • Double release is a no-op — calling release() more than once is safe and must not affect other subscribers.
  • emit forwards args verbatim — whatever you pass after the event name reaches each callback as positional arguments.
  • Synchronous — every live subscriber runs to completion before emit returns. Don't worry about async fan-out, once, or wildcards.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium