EventKit.ts 6.01 KB
export class Disposable {
    private _disposed = false;
    private _disposalAction: () => void;

    constructor(disposalAction: () => void) {
        this._disposalAction = disposalAction;
    }

    get disposed() {
        return this._disposed;
    }

    dispose() {
        if (!this.disposed) {
            this._disposed = true;
            if (this._disposalAction) {
                this._disposalAction();
                this._disposalAction = null;
            }
        }
    }
}

export class CompositeDisposable {
    private _disposed = false;
    private _disposables: Set<Disposable> = new Set();

    constructor(...disposables: Disposable[]) {
        for (let disposable of disposables) {
            this.add(disposable);
        }
    }

    add(disposable: Disposable) {
        if (!this._disposed) {
            if (disposable) {
                this._disposables.add(disposable);
            }
        }
    }

    remove(disposable: Disposable) {
        if (!this._disposed) {
            this._disposables.delete(disposable);
        }
    }

    clear() {
        if (!this._disposed) {
            this._disposables.clear();
        }
    }

    dispose() {
        if (!this._disposed) {
            this._disposed = true;
            this._disposables.forEach((disposable) => {
                disposable.dispose();
            });
            this._disposables = null;
        }
    }
}

export class Event<T extends (...args: any[]) => any> {
    private emitter: Emitter;
    private name: string | symbol;

    constructor(emitter: Emitter, name: string | symbol) {
        this.emitter = emitter;
        this.name = name;
    }

    getName() {
        return this.name;
    }

    toString() {
        return `[Event ${this.name.toString()}]`;
    }

    on(handler: T, unshift: boolean = false) {
        return this.emitter.on(this.name, handler, unshift);
    }

    once(handler: T, unshift: boolean = false) {
        return this.emitter.once(this.name, handler, unshift);
    }

    readonly emit = ((...argsparam: any[]): Promise<ReturnType<T>>[] => {
        argsparam.unshift(this.name);
        return this.emitter.emit.apply(this.emitter, argsparam);
    }) as T;
}

export class Emitter {
    private _disposed = false;
    private _handlersByEventName: Map<string | symbol, Array<(...args: any[]) => any>> = new Map();

    get disposed() {
        return this._disposed;
    }

    clear() {
        this._handlersByEventName.clear();
    }

    dispose() {
        this._handlersByEventName = null;
        this._disposed = true;
    }

    createEvent<T extends (...args: any[]) => void>(eventName?: string | symbol) {
        if (!eventName) {
            return new Event<T>(this, Symbol("Event"));
        }
        return new Event<T>(this, eventName);
    }

    on(eventName: string | symbol, handler: (...args: any[]) => any, unshift: boolean = false) {
        if (this._disposed) {
            throw new Error("Emitter has been diposed");
        }

        let currentHandler = this._handlersByEventName.get(eventName);
        if (currentHandler) {
            if (unshift) {
                currentHandler.unshift(handler);
            } else {
                currentHandler.push(handler);
            }
        } else {
            this._handlersByEventName.set(eventName, [handler]);
        }
        return new Disposable(this.off.bind(this, eventName, handler));
    }

    once(eventName: string | symbol, handler: (...args: any[]) => any, unshift: boolean = false) {
        let wrapped = (...args: any[]) => {
            disposable.dispose();
            return handler(...args);
        };
        let disposable = this.on(eventName, wrapped, unshift);
        return disposable;
    }

    private off = (eventName: string | symbol, handlerToRemove: (...args: any[]) => any) => {
        if (this._disposed) return;
        let oldHandlers = this._handlersByEventName.get(eventName);
        if (oldHandlers) {
            let newHandlers = [];
            for (let handler of oldHandlers) {
                if (handler != handlerToRemove) {
                    newHandlers.push(handler);
                }
            }
            if (newHandlers.length) {
                this._handlersByEventName.set(eventName, newHandlers);
            } else {
                this._handlersByEventName.delete(eventName);
            }
        }
    };

    emit(eventName: string | symbol, ...args: any[]) {
        let handlers = this._handlersByEventName.get(eventName);
        let result: Promise<any>[] = [];
        if (handlers) {
            for (let handler of handlers) {
                try {
                    let ret = handler(...args);
                    if (ret === undefined) {
                        result.push(Promise.resolve());
                    } else if (ret instanceof Promise) {
                        result.push(ret);
                    } else {
                        result.push(Promise.resolve(ret));
                    }
                } catch (e) {
                    console.error(e);
                }
            }
        }
        return result;
    }

    getEventNames() {
        let ret: string[] = [];
        let keys = Object.keys(this._handlersByEventName);
        for (let k of keys) {
            if (typeof (k) === 'string') {
                ret.push(k);
            }
        }
        return ret;
    }

    getEventSymbols() {
        let ret: symbol[] = [];
        let keys = Object.keys(this._handlersByEventName);
        for (let k of keys) {
            if (typeof (k) === 'symbol') {
                ret.push(k);
            }
        }
        return ret;
    }

    listenerCountForEventName(eventName: string | symbol) {
        let handlers = this._handlersByEventName.get(eventName);
        return handlers ? handlers.length : 0;
    }

    getTotalListenerCount() {
        let result = 0;
        let keys = Object.keys(this._handlersByEventName);
        for (let eventName of keys) {
            result += this._handlersByEventName.get(eventName).length;
        }
        return result;
    }
}