JavaScript's standard library doesn't have any direct event emitter support. There are lots of event emitter libraries you can find with a search, but none of them is built-in.
You can:
Use one of the pre-built, pre-tested event emitter libraries that already exist (look for "event emitter" or "publish/subscribe" or "pub/sub").
Use something built into the environment you're running your code in. Node.js has an EventEmitter
class. On browsers, you could create a DOM element that you keep in your class instance (no need for it to be in the document) and use it to store event listeners, then use dispatchEvent
to send events to it. (Though that's not really simpler than the next option.)
Build your own by maintaining an array or Set
of event listeners for each event type that you loop through and call when you need to emit an event.
All three are viable options.
But to give you an idea, a class with very basic event handling looks like this:
class Example {
#eventHandlers = {
"some-event": new Set(),
"another-event": new Set(),
};
on(eventName, handler) {
const handlers = this.#eventHandlers[eventName];
if (!handlers) {
throw new Error(`Invalid event name "${eventName}"`);
}
handlers.add(handler);
}
off(eventName, handler) {
const handlers = this.#eventHandlers[eventName];
if (!handlers) {
throw new Error(`Invalid event name "${eventName}"`);
}
handlers.delete(handler);
}
#trigger(eventName, ...eventArgs) {
const handlers = this.#eventHandlers[eventName];
// assert(!!handlers);
for (const handler of handlers) {
try {
handler(...eventArgs);
} catch { // (Add (e) if your environment doesn't support optional catch bindings)
}
}
}
doSomething() {
// In this example, "doing something" triggers the `some-event` event
this.#trigger("some-event", "something happened");
}
}
const e = new Example();
e.on("some-event", (msg) => console.log(`Handler one: received "${msg}"`));
e.on("some-event", (msg) => console.log(`Handler two: received "${msg}"`));
e.doSomething();
That uses relatively modern things (private fields, optional catch bindings), but you can adapt it as needed.
There are a dozen or more ways to skin this cat. Cross-talk between nandlers, cancelling events, passing in some kind of Event
object, etc., etc., etc. But the fundamentals are:
- Registering handlers (
on
in the example)
- Unregistering handlers (
off
in the example)
- Triggering events (
#trigger
in the example)