JavaScript 自定义事件系统

自定义事件系统

  • 下面是一个简单的自定义事件系统,内部提供了(比较)完善的类型推导。
  • 继承使用或者直接使用都行。

代码实现(Typescript / JavaScript)

type InputParams<Fn> = Fn extends ((...args: infer P) => any) ? P : never[]
type ReturnParams<Fn> = Fn extends ((...args: any[]) => infer P) ? P : never[]
/**
 * 自定义事件系统,继承使用
 * 添加的事件监听函数listener可以通过返回 remove-listener 字符串来移除自身(类型与“once”参数,但比它要更灵活)
 */
export class AbstractEvent<EListener> {
    protected readonly _eventHandlers = new Map<keyof EListener, Set<EListener[keyof EListener]>>();

    // 触发事件前的回调函数,默认为空函数,子类可以通过覆盖实现添加自定义逻辑
    protected beforeDispatch<T extends keyof EListener>(type: T, args: InputParams<EListener[T]>): InputParams<EListener[T]> {
        return args;
    }

    // 触发某个事件
    dispatch<T extends keyof EListener>(type: T, ...args: InputParams<EListener[T]>): ReturnParams<EListener[T]>[] | undefined {
        let set = this._eventHandlers.get(type);
        if (set) {
            const deleteSet: typeof set = new Set();
            const returns: any[] = [];
            let returnValue: any;

            args = this.beforeDispatch(type, args);
            // 事件派发
            for (let listener of set) {
                if (typeof listener === 'function') {
                    returnValue = listener(...args);
                    if (returnValue === 'remove-listener') deleteSet.add(listener);
                    else returns.push(returnValue);
                } else console.error('EventController.dispatch listener is not a function');
            }
            // 移除不再需要的监听函数
            if (deleteSet.size > 0) {
                for (let listener of deleteSet) set.delete(listener);
                if (set.size === 0) this._eventHandlers.delete(type);
            }
            this.afterDispatch(type, args);
            return returns as any;
        } else return undefined;
    }

    // 触发事件后的回调函数,默认为空函数,子类可以通过覆盖实现添加自定义逻辑
    protected afterDispatch<T extends keyof EListener>(type: T, args: InputParams<EListener[T]>): void {}

    // 添加事件监听函数前的回调函数,默认为空函数,子类可以通过覆盖实现添加自定义逻辑
    protected beforeAddListener<T extends keyof EListener>(type: T, handler: EListener[T]): void {}

    // 添加事件监听函数
    addListener<T extends keyof EListener>(type: T, handler: EListener[T]): void {
        let set = this._eventHandlers.get(type);
        if (!set) this._eventHandlers.set(type, set = new Set<EListener[keyof EListener]>());
        this.beforeAddListener(type, handler);
        set.add(handler);
        this.afterAddListener(type, handler);
    }

    // 添加事件监听函数后的回调函数,默认为空函数,子类可以通过覆盖实现添加自定义逻辑
    protected afterAddListener<T extends keyof EListener>(type: T, handler: EListener[T]): void {}

    // 移除事件监听函数前的回调函数,默认为空函数,子类可以通过覆盖实现添加自定义逻辑
    protected beforeRemoveListener<T extends keyof EListener>(type: T, handler: EListener[T]): void {}

    // 移除事件监听函数
    removeListener<T extends keyof EListener>(type: T, handler: EListener[T]): void {
        let set = this._eventHandlers.get(type);
        if (set) {
            this.beforeRemoveListener(type, handler);
            set.delete(handler);
            if (set.size === 0) this._eventHandlers.delete(type);
            this.afterRemoveListener(type, handler);
        }
    }

    // 移除事件监听函数后的回调函数,默认为空函数,子类可以通过覆盖实现添加自定义逻辑
    protected afterRemoveListener<T extends keyof EListener>(type: T, handler: EListener[T]): void {}
}

使用指南

  • 使用时需要传入一个泛型来辅助类型推到,该泛型应该是一个字典类型接口,demo如下所示:
interface MeSqlEvent {
    // before 数据库连接
    Before_connect: (self: MySQL) => void
    // 数据库连接失败
    Error_connect: (self: MySQL) => void
    // after 数据库连接
    After_connect: (self: MySQL) => void
    // before 关闭数据库连接
    Before_disConnect: (self: MySQL) => void
    // 关闭数据库连接失败
    Error_disConnect: (self: MySQL) => void
    // after 关闭数据库连接
    After_disConnect: (self: MySQL) => void
    // 切换数据库
    Change_database: (self: MySQL, database: string) => void
}
  • 接口的key是事件名称,value是事件所对应的listener函数接口,AbstractEvent会通过该泛型实现比较强大的类型推导。
  • 使用时推荐继承:
class MySQL extends AbstractEvent<MeSqlEvent> {

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值