0

Given a need for a class to emit multi-listener events with typed payloads, and the following restrictions:

  • No ability to inherit a different class, like what the Node.js EventEmitter requires (the class in question already extends something, and there's no possibility to create a mixin in ES6)
  • A need for encapsulation, in the way that only if the listener has a direct reference to the object, can it listen to its events (i.e. no global access to listen to the event, like what using standard JS/TS CustomEvent would result in)
  • Ability to listen to an event per-object (i.e. only on a specific instance of the class, not based on an event's string identifier - again the only thing that using CustomEvent would allow)

, what is the best approach/package/API to use?

Essentially, what I am looking for is something like this:

/** There is a class called ProcessDoer, which does some kind of work,
 and emits an event once in a while, when the process is done 
(in this example it's just an interval for simplicity).
It has a member variable onProcessDone, which is an event, 
the class of which I would either want to write, 
or if something like this exists in an npm package, know its name. */

class BaseClass {
    public someProperty: number;
}

class ProcessDoer extends BaseClass {
    constructor() {
        super();

        setInterval(() => {
            this.someProperty = Math.random();
            this.onProcessDone.emit(this.someProperty);
        }, 1000);
    }

    public onProcessDone: SomeTypeOfEventEmitter<{ result: number }>;
}

const processDoer1 = new ProcessDoer();
const processDoer2 = new ProcessDoer();

/** In the example, the non-existent `SomeEventEmitterType` has a very
similar structure to `CustomEvent`, i.e. it's declared in a templated way,
with a specific payload variable name and type.
However, the callback isn't registered in a global way, like
`addEventListener`, but specifically on the exact instance needed. */
processDoer2.onProcessDone.addCallback((result: number) => {
    console.log("The second ProcessDoer finished process with result", result);
});

Moritz Ringler
  • 9,772
  • 9
  • 21
  • 34
goose_lake
  • 847
  • 2
  • 15
  • 1
    Don't have a complete answer, but what about returning an Observable? https://rxjs.dev/guide/observable Observable is a pretty useful basic type, can do what an EventEmitter does, and may even be a built-in JS type at some point. – Trevor Dixon Feb 22 '23 at 18:17
  • Thank you for the suggestion, will try it out. I am not familiar with this exact library. Is there a way to call the `subscriber.next()` function from outside the constructor of the `Observable`? If I make a new instance and save a reference to it in a variable, can I invoke a function for all subscribers at a later stage, from a different place, just having that reference? – goose_lake Feb 23 '23 at 12:36
  • 1
    https://rxjs.dev/guide/subject Subject looks more appropriate. It's a type of Observable, but it specifically says it's like EventEmitter. Looks like it has the properties you want. – Trevor Dixon Feb 23 '23 at 12:53
  • Thank you for the answer, Trevor! This does indeed look like exactly what I needed. If you want to add this as an answer, I will mark as correct. – goose_lake Feb 23 '23 at 14:22
  • The OP might have a look at ... [_"How to implement an event dispatching system for ES/JS object types?"_](https://stackoverflow.com/q/73894457/2627243) – Peter Seliger Jun 13 '23 at 11:51
  • Thank you for the comment. In many OOP tasks I have met, muticast events with typed payloads are an indispensible tool for proper encapsulation and loose coupling. Coming from many other languages where this is a basic feature of either the language runtime or a standard library, it's a shock for me that the world's most popular language doesn't offer this feature without hard-to-find third-party libraries, or a need for a custom implementation (writing one requires an understanding of the language's specifics to degree most people don't go to, but almost anyone might need it as a tool). – goose_lake Jun 14 '23 at 10:02

0 Answers0