// import { EventEmitter } from 'eventemitter3'; import { EventType } from './eventType'; const EventEmitter = require('eventemitter3'); // 9 executes first, then 8, then ... 0 export type Priority = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; export type Clone = { [K in keyof T]: T[K]; }; const EVENT_STOPPED = {}; // we can also use a symbol, but the compatibility is not good function sortEventListeners( emitter: any, event: string | symbol, handler: any, priority: Priority, ) { // 优先级为 0 时,不需要再排序 if (priority === 0) { return; } const listeners = emitter._events[event]; if (listeners.fn) { listeners.priority = priority; return; } const newAddedListener = (listeners as any[]).find(l => l.fn === handler); newAddedListener.priority = priority; // 排序会影响当前正在响应的 listeners,所以需要拷贝 emitter._events[event] = listeners .slice() .sort((a: any, b: any) => (b.priority || 0) - (a.priority || 0)); } function monkeyPatch(emitter: any) { const e = emitter as any; // from https://github.com/primus/eventemitter3/blob/master/index.js#L166 e.emit = function emit( event: any, a1: any, a2: any, a3: any, a4: any, a5: any, ) { const prefix = (EventEmitter as any).prefixed; const evt = prefix ? prefix + event : event; if (!this._events[evt]) { return false; } const listeners = this._events[evt]; const len = arguments.length; let args; let i; let isStopped = false; if (listeners.fn) { if (listeners.once) { this.removeListener(event, listeners.fn, undefined, true); } switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; default: } for (i = 1, args = new Array(len - 1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { const length = listeners.length; let j; for (i = 0; i < length; i++) { if (listeners[i].once) { this.removeListener(event, listeners[i].fn, undefined, true); } switch (len) { case 1: isStopped = listeners[i].fn.call(listeners[i].context) === EVENT_STOPPED; break; case 2: isStopped = listeners[i].fn.call(listeners[i].context, a1) === EVENT_STOPPED; break; case 3: isStopped = listeners[i].fn.call(listeners[i].context, a1, a2) === EVENT_STOPPED; break; case 4: isStopped = listeners[i].fn.call(listeners[i].context, a1, a2, a3) === EVENT_STOPPED; break; default: if (!args) { for (j = 1, args = new Array(len - 1); j < len; j++) { args[j - 1] = arguments[j]; } } isStopped = listeners[i].fn.apply(listeners[i].context, args) === EVENT_STOPPED; } if (isStopped) { break; } } } return true; }; } export interface Listener { (payload: Payload): void; } interface ListenerConfig { type: EventType; handler: Listener; priority?: Priority; } export class EventBus< ReadableEvents extends EventType = EventType, WritableEvents extends EventType = EventType > { private static instance: EventBus; private _emitter: any; constructor() { this._emitter = new EventEmitter(); monkeyPatch(this._emitter); } public emit>(type: T): this; public emit(type: T, payload: T['payload']): this; public emit(type: any, payload?: any) { this._emitter.emit(type.symbol, payload); return this; } public on( type: T, handler: Listener, priority: Priority = 0, ) { const listeners = (this._emitter as any)._events[type.symbol]; if (listeners) { let findedListener: any = undefined; if (Array.isArray(listeners)) { findedListener = (listeners as any[]).find(l => l.fn === handler); } else { if (listeners.fn === handler) { findedListener = listeners; } } if (findedListener) { if (process.env.NODE_ENV === 'development') { console.warn( `listener for ${type.description} already has one same listener, ignored.`, ); } return this; } } this._emitter.on(type.symbol, handler); sortEventListeners(this._emitter, type.symbol, handler, priority); return this; } public off( type: T, handler: Listener, ) { this._emitter.off(type.symbol, handler); return this; } /** * Return the listeners registered for a given event. */ public listeners(event: T) { return this._emitter.listeners(event.symbol); } public getSession() { return new EventBusSession(this); } public static EVENT_STOPPED = EVENT_STOPPED; static getInstance(): EventBus { if (!this.instance) { this.instance = new EventBus(); } return this.instance; } static getSession(): EventBusSession; static getSession< ReadableEvents extends EventType, WritableEvents extends EventType >( eventBus: EventBus, ): EventBusSession; static getSession< ReadableEvents extends EventType, WritableEvents extends EventType >( eventBus: EventBusSession, ): EventBusSession; static getSession( eventBus: EventBus | EventBusSession = EventBus.getInstance(), ): EventBusSession { if (eventBus instanceof EventBusSession) { return new EventBusSession(eventBus.rawEventBus); } else { return new EventBusSession(eventBus); } } } export class EventBusSession< ReadableEvents extends EventType = EventType, WritableEvents extends EventType = EventType > { private listenters: Array> = []; rawEventBus: EventBus; constructor(public eventBus: EventBus) { this.rawEventBus = eventBus; } public on( type: T, handler: Listener, priority: Priority = 0, ) { this.listenters.push({ type, handler, priority, }); this.rawEventBus.on(type, handler, priority); return this; } public off( type: T, handler: Listener, ) { this.rawEventBus.off(type, handler); return this; } public emit>(type: T): this; public emit(type: T, payload: T['payload']): this; public emit(...args: any[]) { this.rawEventBus.emit(args[0], args[1]); return this; } public clear() { this.listenters.forEach(({ type, handler }) => this.rawEventBus.off(type as ReadableEvents, handler), ); } }