RxJS Mastery – #12 fromEventPattern

RxJS fromEventPattern lesson title

Similar to the fromEvent operator the RxJS fromEventPattern operator creates Observables from events. But in the pattern case it supports an arbitrary API for registering event handlers. This API looks as follows:

// addHandler
(handler: NodeEventHandler) => any;

// removeHandler
(handler: NodeEventHandler, signal?: any) => void;

The addHandler needs to be passed to the fromEventPattern operator while the removeHandler is optional. An example could look like this:

const addHandler = (handler) => document.addEventListener('click', handler);
const removeHandler = (handler) => document.removeEventListener('click', handler);

const clicks$ = fromEventPattern(addHandler, removeHandler);

Eventually the functionality is similar to fromEvent but the constraints on the passed functions is more lenient.

RxJS fromEventPattern explained 🎓

The fromEventPattern is a creation operator and returns an Observables:

fromEventPattern<T>(
    addHandler: (handler: NodeEventHandler) => any, 
    removeHandler?: (handler: NodeEventHandler, signal?: any) => void, 
    resultSelector?: (...args: any[]) => T
): Observable<T | T[]>

Beside the optional removeHandler it also offers a resultSelector to directly transform the output. The NodeEventHandler in this case is just defined as follows:

export type NodeEventHandler = (...args: any[]) => void;

This means it’s a function taking arbitrary arguments and returning void. Of course you should make sure that the actual function you pass somehow attaches the handler to the source of the events. In the example from the first section the handler is passed to the document.addEventListener.

What problems does the RxJS fromEventPattern operator solve? 🚧

As said, the API of fromEventPattern is more flexible than the one of fromEvent. All fromEvent functions could be built by fromEventPattern. Of course both operators make sense and both have their applications.

One specific use case is handling an API with a cancellation token. In this case the addHandler returns a token. To cancel the operation of the API you need to send this token again to the handler for unregistering. Using the RxJS fromEventPattern this looks as follows:

// simplified API
class SomeAPI {
    registerEventHandler(handler: () => void) { return 'token' };
    unregisterEventHandler(token:string) { console.log('operation cancelled')};
}

const someAPI = new SomeAPI();
const token = someAPI.registerEventHandler(function() {});
someAPI.unregisterEventHandler(token);

const someAPIObservable$ = fromEventPattern(
    (handler) => { return someAPI.registerEventHandler(handler); },
    (handler, token) => someAPI.unregisterEventHandler(token),
);

The token is returned from the addHandler and automatically passed by fromEventPattern into the removeHandler as second argument (signal).

How to test the fromEventPattern operator🚦

Again, you don’t really want to test the fromEventPattern operator itself. Therefore you wrap the functionality and test that with rxjs-marbles. Alternatively you can pass mocked handler functions and control your tests through those.

Exercise for the fromEventPattern operator 💪

Create an Observable based on NodeJs EventEmitter with the help of fromEventPattern.