Realizing what you want Emitter

For the process of registering events, we want to do a few things on the following nodes:

  • The first time a listener is registered
  • The first listener is successfully registered
  • When a listener is registered
  • When all listeners are removed

We also need it to have the following capabilities:

  • Remove all listeners at once (not one by one ourselves)removeEventListener)
  • To remove a singlelistener

Therefore, we designed the following structure:

    exportinterface EmitterOptions { onFirstListenerAdd? :Function; // Register the listener callback for the first timeonFirstListenerDidAdd? :Function; // Register a listener to complete the callbackonListenerDidAdd? :Function; // An event is successfully monitoredonLastListenerRemove? :Function; // When the last event is removed
    }
    export class Emitter<T> { private readonly _options? : EmitterOptions;// The event trigger option, when the event is added to the listener and removed, the relevant node, such as: the first listener is added, the last listener is removed.
        private _disposed: boolean = false; // Whether the event bridge has been releasedprivate _event? : Event<T>;// Returns an event registration functionprotected _listeners? : LinkedList<Listener<T>>;// Event listener
        constructor(options? : EmitterOptions) {this._options = options;
        }
         // Get "emitter. On" function
        get event(): Event<T> {
            ...
        }
        // equivalent to: emitters. Emit
        fire(event: T): void{... }// Remove all listenersdispose() { ... }}Copy the code

First of all, the implementation of the event(on function) is as follows (detailed functions will be placed at the bottom, if necessary, you can see) :

    get event(): Event<T> {
        // equivalent to emitters
        if (!this._event) {
            // If no "on" function exists, create one and return it
            this._event = (listener: (e: T) = >any, thisArgs? : any) => {this.initListeners(); // Initialize the Listeners collection
                const firstListener = this._listeners.isEmpty();
                this.onFirstListenerAdd();// When the first listener is added, the callback is executed.
                const remove = this._listeners.push(! thisArgs ? listener : [listener, thisArgs]);// Add a listener and return a function to delete the listener.
                this.onFirstListenerDidAdd();// The first listener is added and the callback is executed
                this.onListenerDidAdd();// When a listener is added, the callback is executed
                return this.eventDispose(remove); // Returns the function that releases this event}}return this._event;
    }
Copy the code

Second, the implementation of the fire(emit function) function is as follows:

    fire(event: T): void {
        if (this._listeners) {
            while (this._listeners.size > 0) {
                const listener = this._listeners.shift()! ;try {
                    if (typeof listener === 'function') {
                        listener.call(undefined, event);
                    } else {
                        listener[0].call(listener[1], event); }}catch (e) {
                    console.log(e); }}}}Copy the code

Finally, dispose function implementation:

    dispose() {
        if (this._listeners) {
            this._listeners.clear();
        }
        this._disposed = true;
        this.onLastListenerRemove();
    }
Copy the code

So far, we’ve implemented an Emitter of our own. Let’s use it:

    const emitter = new Emitter({
        onFirstListenerAdd: (a)= > console.log('onFirstListenerAdd');
        onFirstListenerDidAdd: (a)= > console.log('onFirstListenerDidAdd');
        onListenerDidAdd: (a)= > console.log('onListenerDidAdd');
        onLastListenerRemove: (a)= > console.log('onLastListenerRemove');
    });
    const listener1 = emitter.event((a)= > console.log('This is our own listening callback -1')); // Fire, the callback is executed
    const listener2 = emitter.event((a)= > console.log('This is our own listening callback -2')); // Fire, the callback is executed
    listener1.dispose(); // Remove listener 1
    // emitter.dispoase(); Remove all listeners
    emitter.fire(); // Trigger the event
Copy the code

Ps: Some concrete implementations used in the above process:

    private initListeners() {
        if (!this._listeners) {
            this._listeners = new LinkedList();
        }
    }

    private onFirstListenerAdd(firstListener: Listener) {
        if (firstListener && this._options && this._options.onFirstListenerAdd) {
            this._options.onFirstListenerAdd(this);
        }
    }

    private onFirstListenerDidAdd(listener: Listener) {
        if (firstListener && this._options && this._options.onFirstListenerDidAdd) {
            this._options.onFirstListenerDidAdd(this);
        }
    }

    private onListenerDidAdd(listener: Listener) {
        if (this._options && this._options.onListenerDidAdd) {
            this._options.onListenerDidAdd(this, listener, thisArgs);
        }
    }

    private onLastListenerRemove() {
        if (this._options && this._options.onLastListenerRemove) {
            const hasListeners = (this._listeners && !this._listeners.isEmpty());
            if(! hasListeners) {this._options.onLastListenerRemove(this);
            }
        }
    }

    private eventDispose(remove: function) {
        return {
            dispose: (a)= > {
                if (!this._disposed) {
                    remove();
                    this.onLastListenerRemove(); }}}}Copy the code

How to give ipcMain the capabilities of an Emitter we implemented ourselves