FrontendInterviews.dev

Loading problem…

7. Event Emitter II - Once

Medium•

This problem builds on event-emitter. Complete that first, then load your solution to continue.

Extend EventEmitter with once semantics while keeping dispatch deterministic under mutation.

This is an API-contract problem, not a syntax problem. Interviewers are testing whether you can preserve event ordering and correctness even when listeners are removed during emit.

Requirements

Implement:

class EventEmitter {
  on(event, handler) {}
  once(event, handler) {}
  off(event, handler) {}
  emit(event, ...args) {}
}

Contract

  • on, once, and off must return this for chaining.
  • once(event, handler) registers a listener that can run at most once.
  • off(event, handler) removes all listeners (including once listeners) registered with that handler.
  • emit(event, ...args) executes listeners in registration order and returns an array of return values in that same order.

Mutation Safety During Emit

emit must iterate over a snapshot of listeners for that event. If listeners are removed while dispatch is running (including once self-removal), all listeners that were scheduled at the start of the emit must still run exactly once in order.

Important Edge Cases

  • A once listener must be removable via off(event, handler) before its first execution.
  • Removing a listener during an active emit must not skip or reorder remaining listeners in that cycle.
  • A once listener must never execute more than once, even under nested or re-entrant emits.

Example

const ee = new EventEmitter();

ee.on('sum', (a, b) => a + b);
ee.once('sum', (a, b) => a * b);

ee.emit('sum', 2, 3); // [5, 6]
ee.emit('sum', 2, 3); // [5]

Constraints

  • A call to `emit` must capture a snapshot before execution to avoid iteration bugs during mutation.
  • `once` listeners must be removed exactly after their first invocation.
  • `off(event, handler)` must be able to remove a `once` listener before first emit.
  • Registration APIs must return `this` for chaining.
Accepted32/51|Acceptance Rate62.7%