import type { EventsConstraint, SubscriberEntry } from "./common.ts" import type { InternalSubscriberEntryMap } from "./internal.ts" /** * @description 表示 EventManager 的构造选项。 */ export interface EventManagerOptions> { /** * @description 在订阅者执行出错时接收错误与对应订阅项。 */ onSubscriberError?: ( subscriberEntry: SubscriberEntry, error: unknown, ) => void } /** * @description 管理同一事件表下的订阅、取消订阅与事件派发。 */ export class EventManager> { protected options: EventManagerOptions /** * @description 每一类事件对应一个内部订阅映射,映射中的 key 是订阅者函数,value 是 SubscriberEntry 对象。 */ protected subscribers: { [K in keyof Events]?: InternalSubscriberEntryMap | undefined } constructor(options?: EventManagerOptions) { this.options = options ?? {} this.subscribers = {} } protected internalHandleSubscriberError( subscriberEntry: SubscriberEntry, error: unknown, ): void { if (this.options.onSubscriberError !== undefined) { this.options.onSubscriberError(subscriberEntry, error) } else { console.error( `Error occurred while emitting event "${String(subscriberEntry.event)}":`, error, ) } } /** * @description 检查某个事件是否已经注册给定订阅者。 */ has(event: K, subscriber: Events[K]): boolean { const subscriberEntryMap = this.subscribers[event] if (subscriberEntryMap === undefined) { return false } const has = subscriberEntryMap.has(subscriber) return has } protected internalSubscribe( event: K, subscriber: Events[K], once: boolean, ): SubscriberEntry { const subscriberEntryMap = this.subscribers[event] ?? new Map>() this.subscribers[event] = subscriberEntryMap const existingSubscriberEntry = subscriberEntryMap.get(subscriber) if (existingSubscriberEntry !== undefined) { return existingSubscriberEntry } const unsubscribe = (): void => { this.unsubscribe(event, subscriber) } const newSubscriberEntry: SubscriberEntry = { event, once, subscriber, unsubscribe, } subscriberEntryMap.set(subscriber, newSubscriberEntry) return newSubscriberEntry } /** * @description 为指定事件添加常规订阅者。 */ subscribe(event: K, subscriber: Events[K]): SubscriberEntry { return this.internalSubscribe(event, subscriber, false) } /** * @description 为指定事件添加只触发一次的订阅者。 */ subscribeOnce( event: K, subscriber: Events[K], ): SubscriberEntry { return this.internalSubscribe(event, subscriber, true) } /** * @description 移除指定事件上的给定订阅者。 */ unsubscribe(event: K, subscriber: Events[K]): this { const subscriberEntryMap = this.subscribers[event] if (subscriberEntryMap === undefined) { return this } subscriberEntryMap.delete(subscriber) if (subscriberEntryMap.size === 0) { delete this.subscribers[event] } return this } /** * @description 移除指定事件上的全部订阅者。 */ removeSubscribersOfEvent(event: K): this { delete this.subscribers[event] return this } /** * @description 移除当前管理器上的全部订阅者。 */ removeAllSubscribers(): this { this.subscribers = {} return this } /** * @description 同步派发指定事件。 */ emit(event: K, ...args: Parameters): boolean { const subscriberEntryMap = this.subscribers[event] if (subscriberEntryMap === undefined) { return false } // Create a copy of the subscriber entries to avoid issues when // subscribers are modified during event emission const subscriberEntries = [...subscriberEntryMap.values()] for (const subscriberEntry of subscriberEntries) { try { subscriberEntry.subscriber(...args) } catch (exception) { this.internalHandleSubscriberError(subscriberEntry, exception) } finally { if (subscriberEntry.once === true) { subscriberEntry.unsubscribe() } } } return true } /** * @description 按订阅顺序异步派发指定事件。 */ async emitAsync( event: K, ...args: Parameters ): Promise { const subscriberEntryMap = this.subscribers[event] if (subscriberEntryMap === undefined) { return false } const subscriberEntries = [...subscriberEntryMap.values()] for (const subscriberEntry of subscriberEntries) { try { const result = subscriberEntry.subscriber(...args) await Promise.resolve(result) } catch (exception) { this.internalHandleSubscriberError(subscriberEntry, exception) } finally { if (subscriberEntry.once === true) { subscriberEntry.unsubscribe() } } } return true } /** * @description 并发等待所有订阅者完成后结束异步派发。 */ async emitConcurrentAsync( event: K, ...args: Parameters ): Promise { const subscriberEntryMap = this.subscribers[event] if (subscriberEntryMap === undefined) { return false } const promises = [...subscriberEntryMap.values()].map(async (subscriberEntry) => { try { const result = subscriberEntry.subscriber(...args) await Promise.resolve(result) } catch (exception) { this.internalHandleSubscriberError(subscriberEntry, exception) } finally { if (subscriberEntry.once === true) { subscriberEntry.unsubscribe() } } }) await Promise.all(promises) return true } }