cf.completefrontend

Write the codeMedium

Write an EventEmitter class with on, off, once, and emit.

Before you start

What's an event emitter?

An event emitter is an object that passes news from one part of a program to any other parts that want it. Code that wants to hear about something registers a function, called a listener, under an event name such as "message". Code that has news emits the event by that name, with any data, and the emitter calls every listener registered under it, passing the data along. Neither side needs to know about the other: the code that emits doesn't know who's listening, or whether anyone is, and listeners can be added and removed without touching it.

Think of a radio station. It broadcasts on its frequency whether anyone has tuned in or not, and every radio tuned to it hears the same thing. Tuning in is on, broadcasting is emit, and switching off is off.

Node has one built in, EventEmitter from the node:events module, and its four main methods are the ones most emitters share:

MethodWhat it does
on(name, listener)Adds listener for the event name. An event can have any number of listeners.
once(name, listener)Adds a listener that runs the next time name is emitted, and is then removed.
off(name, listener)Removes a listener. You pass the same function you added, so keep a reference to it: an arrow function written inline can't be removed later. Some emitters also have on return a function that removes the listener, so you don't need to keep it.
emit(name, ...args)Calls each of the event's listeners with args, one after another in the order they were added, before emit returns. In Node, a listener written as a regular function gets the emitter as this, and emit returns true if the event had listeners and false if it had none.
const { EventEmitter } = require("node:events");
 
const chat = new EventEmitter();
const greet = (name) => console.log("Hello, " + name);
 
chat.on("join", greet);
chat.once("join", (name) => console.log(name + " was first"));
 
chat.emit("join", "Ada");   // logs "Hello, Ada", then "Ada was first"; returns true
chat.emit("join", "Alan");  // logs "Hello, Alan" only: the once listener is gone
chat.off("join", greet);
chat.emit("join", "Grace"); // logs nothing and returns false: no listeners are left

In the browser, the same idea is EventTarget, which every element, document, and window inherit from. addEventListener(type, listener) is its on, removeEventListener(type, listener) its off, and addEventListener(type, listener, { once: true }) its once. The browser emits events such as click and input itself, and your code can emit its own with dispatchEvent. Node's streams and servers are EventEmitters too, which is why you write stream.on("data", ...).

A publish/subscribe hub, pub/sub for short, is the same idea under other names. Event names are called topics, often strings such as "user.created"; adding a listener is subscribing, and emitting is publishing. Some hubs also let one subscription match a whole group of topics with a wildcard pattern. An event bus is one emitter or hub that many parts of an app share, so they can talk to each other without importing each other.

on(event, listener) adds a listener for event and returns a function that removes it.

once(event, listener) adds a listener that runs on the next emit of event only.

off(event, listener) removes a listener, including one added with once.

emit(event, ...args) calls the event's listeners with args, in the order they were added, with this set to the emitter. It returns true if the event had any listeners, and false otherwise. Listeners added or removed during an emit don't change who runs in that emit.

Write this
class EventEmitter {
  // your code
}
 
// const emitter = new EventEmitter();
// const stop = emitter.on("message", (text) => console.log(text));
// emitter.once("message", () => console.log("first message"));
// emitter.emit("message", "hi");  // logs "hi", then "first message", and returns true
// emitter.emit("message", "hey"); // logs "hey" only: the once listener is gone
// stop();
// emitter.emit("message", "hi");  // logs nothing and returns false

Solve it in the editor, with tests

The answer

Write it yourself first. Then compare it with a reference solution and the checks it passes.

Checking your sign-in…