import { consts } from '../consts' import { tsEntry } from '../entry/entry' import { tsLog } from '../logger/logger' import { tsString } from '../utils/string' /** * 事件模块 */ export module tsEvent { /** * 事件对象 */ export class Event extends tsEntry.Entry { private handler: (event: Event, caller: any, ...args: any[]) => void = null private caller: any = null private _execLimit: number = -1 get execLimit() { return this._execLimit } private _execCount: number = 0 get execCount() { return this._execCount } constructor(id: number, name: string, handler: (event: Event, caller: any, ...args: any[]) => void, caller: any = null, limit: number = -1) { super(id, name, 0) this.entryName = 'Event' this.handler = handler this.caller = caller this._execLimit = limit } destroy(): void { this.handler = null this.caller = null } equal(handler: (event: Event, caller: any, ...args: any[]) => void, caller: any): boolean { return handler == this.handler && caller == this.caller } async _dispatch(...args: any[]) { this.handler(this, this.caller, ...args) } dispatch(...args: any[]): boolean { if (!this.handler) { return false } this._dispatch(...args) this._execCount++ return true } checkLimit(): boolean { if (this.execLimit == -1) { return false } return this.execCount >= this.execLimit } } export class EventManager { private _name: string = '' get name() { return this._name } set name(value: string) { this._name = value } get fullName() { return `EventManager[${this._name}]` } private autoIncIdStart: number = 0 private nameEventsMap: Map = null constructor(name: string) { this._name = name this.autoIncIdStart = consts.autoIncIdStart this.nameEventsMap = new Map() } has(name: string, handler: (event: Event, caller: any, ...args: any[]) => void, caller: any): boolean { let events = this.nameEventsMap.get(name) if (events) { let len = events.length for (let index = 0; index < len; index++) { const element = events[index] if (element.equal(handler, caller)) { return true } } } return false } get(name: string, id: number): Event { let events = this.nameEventsMap.get(name) if (events) { let len = events.length for (let index = 0; index < len; index++) { const element = events[index] if (element.id == id) { return element } } } return null } add(name: string, handler: (event: Event, caller: any, ...args: any[]) => void, caller: any = null, limit: number = -1): Event { if (this.has(name, handler, caller)) { tsLog.Logger.errorf(this.fullName + ' addEventListener {0} err:duplicate add', name) return null } let id = this.autoIncIdStart this.autoIncIdStart++ let event = new Event(id, name, handler, caller, limit) if (this.nameEventsMap.has(name)) { this.nameEventsMap.get(name).push(event) } else { this.nameEventsMap.set(name, [event]) } event.entryName = tsString.format('{0}[{1}]', event.entryName, this.name) tsLog.Logger.debugf(this.fullName + ' addEventListener {0} size:{1}', event.fullName, this.nameEventsMap.get(name).length) return event } del(event: Event): boolean { if (!event || !event.id) { tsLog.Logger.warnf(this.fullName + ' delEventListener {0} err:invalid instance', event) return } let events = this.nameEventsMap.get(event.name) if (events) { let len = events.length for (let index = 0; index < len; index++) { const element = events[index] if (element.id == event.id) { element.destroy() events.splice(index, 1) tsLog.Logger.debugf(this.fullName + ' delEventListener {0} size:{1}', event.fullName, events.length) return true } } } return false } delById(name: string, id: number): boolean { let event = this.get(name, id) return this.del(event) } clear(name: string): boolean { let arr = this.nameEventsMap.get(name) let len = arr ? arr.length : 0 for (let index = 0; index < len; index++) { arr[index].destroy() } return this.nameEventsMap.delete(name) } dispatch(name: string, ...args: any[]): void { let events = this.nameEventsMap.get(name) if (!events || events.length == 0) { return } let begin = Date.now() // TODO:这里先用拷贝的方案解决事件回调中删除的问题 const eventsCopy = Object.assign([], events) let len = eventsCopy.length let dels: Event[] = [] for (let index = 0; index < len; index++) { const element = eventsCopy[index] element.dispatch(...args) if (element.checkLimit()) { dels.push(element) } } len = dels.length for (let index = 0; index < len; index++) { const element = dels[index] this.del(element) } let use = Date.now() - begin if (use >= tsLog.ExecTimeLevel.Error) { tsLog.Logger.errorf(this.fullName + ' dispatchEvent {0} exec:{1} dels:{2} use:{3}', name, eventsCopy.length, len, use) } else if (use >= tsLog.ExecTimeLevel.Warn) { tsLog.Logger.warnf(this.fullName + ' dispatchEvent {0} exec:{1} dels:{2} use:{3}', name, eventsCopy.length, len, use) } else { tsLog.Logger.tracef(this.fullName + ' dispatchEvent {0} exec:{1} dels:{2} use:{3}', name, eventsCopy.length, len, use) } } getStatistics(): string[] { let mgrName = this.name let arr: string[] = [] this.nameEventsMap.forEach(function (events: Event[], key: string) { if (events.length == 0) { return } let count = 0 events.forEach(function (value: Event, index: number) { if (value.execCount > count) { count = value.execCount } }) let str = tsString.format('{0} 名称:{1} 数量:{2} 执行计数:{3}', mgrName, key, events.length, count) arr.push(str) }) return arr } } /** * 事件管理实例容器 */ const eventMgrs: EventManager[] = new Array() /** * 全局事件管理实例 */ const globalEventMgr: EventManager = new EventManager('Global') /** * 添加事件管理实例 * @param mgr EventManager实例 */ export function addEventMgr(mgr: EventManager): boolean { for (let index = 0; index < eventMgrs.length; index++) { if (eventMgrs[index].name == mgr.name) { tsLog.Logger.errorStackf('tsEvent addEventMgr {0} err:duplicate add', mgr.name) return false } } eventMgrs.push(mgr) return true } /** * 删除事件管理实例 * @param mgr EventManager实例 * @returns 成功:true 失败:false */ export function delEventMgr(mgr: EventManager): boolean { for (let index = 0; index < eventMgrs.length; index++) { if (eventMgrs[index].name == mgr.name) { eventMgrs.splice(index, 1) return true } } return false } /** * 通过名称删除事件管理实例 * @param name EventManager实例名称 * @returns 成功:true 失败:false */ export function delEventMgrByName(name: string): boolean { for (let index = 0; index < eventMgrs.length; index++) { if (eventMgrs[index].name == name) { eventMgrs.splice(index, 1) return true } } return false } /** * 添加全局事件监听 * @param name 名称 * @param handler 事件处理 * @param caller 调用者(回传参数,可用于解决this指针问题) * @param limit 执行次数(次数达到自动删除,默认-1不限次数) * @returns Event实例 */ export function addEventListener(name: string, handler: (event: Event, caller: any, ...args: any[]) => void, caller: any = null, limit: number = -1): Event { return globalEventMgr.add(name, handler, caller, limit) } /** * 删除全局事件监听 * @param event Event实例 * @returns 成功:true 失败:false */ export function delEventListener(event: Event): boolean { return globalEventMgr.del(event) } /** * 通过唯一标识删除全局事件监听 * @param name 名称 * @param id 唯一标识 * @returns 成功:true 失败:false */ export function delEventListenerById(name: string, id: number): boolean { return globalEventMgr.delById(name, id) } /** * 清理指定名称全局事件监听 * @param name 名称 * @returns 成功:true 失败:false */ export function clearEventListener(name: string): boolean { return globalEventMgr.clear(name) } /** * 派发全局事件 * @param name 名称 * @param args 事件参数列表 * @returns */ export function dispatchEvent(name: string, ...args: any[]): void { return globalEventMgr.dispatch(name, ...args) } /** * 获取统计信息(全局+EventEntry实例) * @returns ["名称:Req_User_Info 数量:1 执行计数:0", ...] */ export function getStatistics(): string[] { let arr = globalEventMgr.getStatistics() eventMgrs.forEach(function (mgr: EventManager, index: number) { arr = arr.concat(mgr.getStatistics()) }) return arr } /** * 局部事件管理基类 */ export class EventEntry extends tsEntry.Entry { private eventMgr: EventManager = null constructor(id: number = 0, name: string = '', typ: number = 0) { super(id, name, typ) this.entryName = 'EventEntry' this.eventMgr = new EventManager(name) addEventMgr(this.eventMgr) } /** * 添加事件监听 * @param name 名称 * @param handler 回调 * @param caller 调用者(回传参数,可用于解决this指针问题) * @param limit 执行次数(次数达到自动删除,默认-1不限次数) * @returns Event实例 */ addEventListener(name: string, handler: (event: Event, caller: any, ...args: any[]) => void, caller: any = null, limit: number = -1): tsEvent.Event { return this.eventMgr.add(name, handler, caller, limit) } /** * 删除事件监听 * @param event Event实例 * @returns 成功:true 失败:false */ delEventListener(event: Event): boolean { return this.eventMgr.del(event) } /** * 通过唯一标识删除事件监听 * @param name 名称 * @param id 唯一标识 * @returns 成功:true 失败:false */ delEventListenerById(name: string, id: number): boolean { return this.eventMgr.delById(name, id) } /** * 清理指定名称事件监听 * @param name 名称 * @returns 成功:true 失败:false */ clearEventListener(name: string): boolean { return this.eventMgr.clear(name) } /** * 派发事件 * @param name 名称 * @param args 事件参数列表 * @returns */ dispatchEvent(name: string, ...args: any[]): void { return this.eventMgr.dispatch(name, ...args) } } }