← All tasksJavaScript · Senior

EventEmitter

Write an EventEmitter class with these methods:

on(event, listener) subscribes and returns an unsubscribe function. off(event, listener) unsubscribes. once(event, listener) subscribes for a single call. emit(event, ...args) calls listeners in subscription order and returns true if there were any, false otherwise.

The same listener can be subscribed twice, and then it's called twice. If a listener unsubscribes another one during emit, the current emit still calls everyone who was subscribed when it started.

Tests (7)

  1. const e = new EventEmitter();
    const f = fn();
    e.on('msg', f);
    e.emit('msg', 1, 2);
    expect(f).toHaveBeenCalledWith(1, 2);
  2. const e = new EventEmitter();
    const a = fn();
    const b = fn();
    e.on('x', a);
    e.on('x', b);
    expect(e.emit('x')).toBe(true);
    expect(e.emit('y')).toBe(false);
    expect(a).toHaveBeenCalledTimes(1);
    expect(b).toHaveBeenCalledTimes(1);
  3. const e = new EventEmitter();
    const f = fn();
    e.on('x', f);
    e.off('x', f);
    e.emit('x');
    expect(f).toHaveBeenCalledTimes(0);
  4. const e = new EventEmitter();
    const f = fn();
    const unsubscribe = e.on('x', f);
    unsubscribe();
    e.emit('x');
    expect(f).toHaveBeenCalledTimes(0);
  5. const e = new EventEmitter();
    const f = fn();
    e.once('x', f);
    e.emit('x', 'a');
    e.emit('x', 'b');
    expect(f).toHaveBeenCalledTimes(1);
    expect(f).toHaveBeenLastCalledWith('a');
  6. const e = new EventEmitter();
    const f = fn();
    e.on('x', f);
    e.on('x', f);
    e.emit('x');
    expect(f).toHaveBeenCalledTimes(2);
  7. const e = new EventEmitter();
    const calls = [];
    const second = () => calls.push('second');
    const first = () => { calls.push('first'); e.off('x', second); };
    e.on('x', first);
    e.on('x', second);
    e.emit('x');
    e.emit('x');
    expect(calls).toEqual(['first', 'second', 'first']);