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 single
listener
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