JS案例:基于发布订阅实现的事件消息中心-MessageCenter

JS案例:基于发布订阅实现的事件消息中心-MessageCenter

「目录」

前言[1]

起步[2]

功能设计[3]

工具函数实现[4]

消息中心类实现[5]

验证功能[6]

写在最后[7]


前言

「之前写过一篇文章:[JS 案例:Observer Pattern(观察者模式)和 Publisher-Subscriber Pattern(发布者/订阅者模式)_DieHunter1024 的博客-CSDN 博客发布/订阅模式和观察者模式一样吗?在许多地方我们都能见到基于这二者或者说基于某种设计模式的框架,函数或插件在浏览器中使用 addEventListener(type,fn)对 dom 元素进行事件委托,事件监听用户的异步操作 Android 中也有一个事件发布/订阅的轻量级框架:EventBus,原理与 web 相似 Socket.io 的许多方法也是基于此类模式,监听与触发事件,批量广播等在 Node 中同样也有一个 events 事件触发器解决异步操作的同步响应![](https://g.csdnimg.cn/static/logo/favicon32.ico “JS 案例:Observer Pattern(观察者模式 “JS 案例:Observer Pattern(观察者模式)和 Publisher-Subscriber Pattern(发布者/订阅者模式)_DieHunter1024 的博客-CSDN 博客发布/订阅模式和观察者模式一样吗?在许多地方我们都能见到基于这二者或者说基于某种设计模式的框架,函数或插件在浏览器中使用 addEventListener(type,fn)对 dom 元素进行事件委托,事件监听用户的异步操作 Android 中也有一个事件发布/订阅的轻量级框架:EventBus,原理与 web 相似 Socket.io 的许多方法也是基于此类模式,监听与触发事件,批量广播等在 Node 中同样也有一个 events 事件触发器解决异步操作的同步响应![“)和 Publisher-Subscriber Pattern(发布者/订阅者模式)_DieHunter1024 的博客-CSDN 博客发布/订阅模式和观察者模式一样吗?在许多地方我们都能见到基于这二者或者说基于某种设计模式的框架,函数或插件在浏览器中使用 addEventListener(type,fn)对 dom 元素进行事件委托,事件监听用户的异步操作 Android 中也有一个事件发布/订阅的轻量级框架:EventBus,原理与 web 相似 Socket.io 的许多方法也是基于此类模式,监听与触发事件,批量广播等在 Node 中同样也有一个 events 事件触发器解决异步操作的同步响应」

「其中简单描述了一下 JavaScript 中发布订阅模式的实现,但是个人觉得其中的方法过于单一,于是想尝试拓展一下,做个简易版的消息中心」

「起步」

「参考 node 中的 events 事件触发器 我总结归类出了以下函数」

  • 「on :注册事件」
  • 「emit:触发事件」
  • 「un:事件销毁」
  • 「once:注册事件,执行后即销毁」
  • 「clear:重置事件列表(消息中心)」
  • 「has:判断事件是否被订阅」
  • 「handlerLength:返回某个事件的监听函数数量」
  • 「watch:与 on 一样,不同点是可以将结果返回至发布者」
  • 「invoke:与 emit 一样,配合 watch 使用,当 watch 中存在异步操作时接收其结果」

「功能设计」

「了解了实现的功能,我们把类的接口实现一下,其中 events 是之前文章中的调度中心,使用一个对象来存取所有绑定的事件」

export declare interface Handlers {
    [key: string]: Array<Function>
}
export declare interface IMessageCenter {
    events: Handlers
    _instance?: IMessageCenter
    on: (typestring, handler: Function) => this
    emit: (typestring, data?: any) => this
    un: (typestring, handler?: Function) => this
    once: (typestring, handler: Function) => this
    clear: () => this
    has: (typestring) => boolean
    handlerLength: (typestring) => number
    watch: (typestring, handler: Function) => this
    invoke: (typestring, data?: any) => Promise<void>
}

工具函数实现

「接口完成后,我们来写一些工具函数,这些函数不需要暴露在外面所以在类中,比如异常处理函数:用于解析参数是否异常;单例函数:返回当前类的实例的单例;批量执行函数:执行 events 中与事件名绑定的函数列表;批量销毁函数:批量销毁调度中心中某个函数集;此外,还有一个函数用于区别 watch、invoke 和 on、emit 的事件类型(type)的字符串混入。」

「异常处理函数:」

    /**
     * 检查参数是否符合标准
     * @param type 事件名
     * @param handler 事件钩子
     */

    private checkHandler(typestring, handler: Function) {
        if (type?.length === 0) {
            throw new Error('type.length can not be 0')
        }
        if (!handler || !type) {
            throw new ReferenceError('type or handler is not defined')
        }
        if (typeof handler !== 'function' || typeof type !== 'string') {
            throw new TypeError(`${handler} is not a function or ${type} is not a string`);
        }
    }

「单例函数:」

    //返回当前类的实例的单例
    static Instance(Fn) {
        if (!Fn._instance) {
            Object.defineProperty(Fn, "_instance", {
                value: new Fn()
            });
        }
        return Fn._instance;
    }

「批量执行函数:」

    // 批量执行调度中心中某个函数集
    private runHandler(type, data) {
        for (let i = 0; i < this.events[type].length; i++) {
            this.events[type][i] && this.events[type][i](data "type][i] && this.events[type][i")
        }
    }

「批量销毁函数:」

    // 批量销毁调度中心中某个函数集
    private unHandler(type, handler) {
        !handler && (this.events[type] = [])
        handler && this.checkHandler(type, handler)
        for (let i = 0; i < this.events[type].length; i++) {
            if (this.events[type][i] && this.events[type][i] === handler) {
                this.events[type][i] = null
            }
        }
    }

「字符串混入:」

    private prefixStr(str) {
        return `@${str}`
    }

「消息中心类实现」

「工具函数实现完成后,我们就可以正式开始实现接口中定义的各种函数了,以下是函数的实现过程,其中 this.events 是事件调度中心,一个以事件 type 为 key 的对象」

  • 「has:」
    // 判断事件是否被订阅
    has(typestring) {
        return !!this.events[type]
    }
  • 「on:」
   /**
     * 注册事件至调度中心
     * @param type 事件类型,特指具体事件名
     * @param handler 事件注册的回调
     */

    on(type, handler) {
        this.checkHandler(type, handler)
        if (!this.has(type)) { //若调度中心未找到该事件的队列,则新建某个事件列表(可以对某个类型的事件注册多个回调函数)
            this.events[type] = []
        }
        this.events[type].push(handler)
        return this
    }
  • 「emit:」
   /**
     * 触发调度中心的某个或者某些该事件类型下注册的函数
     * @param type 事件类型,特指具体事件名
     * @param data 发布者传递的参数
     */

    emit(type, data) {
        if (this.has(type)) {
            this.runHandler(type, data)
        }
        return this
    }
  • 「un:」
    //销毁监听
    un(type, handler) {
        this.unHandler(type, handler)
        return this
    }
  • 「once:」
    // 只注册一次监听,执行即销毁
    once(type, handler) {
        this.checkHandler(type, handler)
        const fn = (...args) => {
            this.un(type, fn);
            return handler(...args)
        }
        this.on(type, fn)
        return this
    }
  • 「clear:」
    // 重置调度中心
    clear() {
        this.events = {}
        return this
    }
  • 「handlerLength:」
    // 一个事件被绑定了多少函数
    handlerLength(typestring) {
        return this.events[type]?.length ?? 0
    }
  • 「watch:」
    // 监听invoke的消息,若handler中进行了计算或者异步操作,会反馈给invoke
    watch(type, handler) {
        this.checkHandler(type, handler)
        const fn = (...args) => {
            this.emit(this.prefixStr(type), handler(...args));
        }
        this.on(type, fn);
        return this
    }
  • 「invoke:」
    // 触发watch事件,并且接收watch处理结果
    invoke(type, data) {
        return new Promise<void>((resolve) => {
            this.once(this.prefixStr(type), resolve);
            this.emit(type, data);
        }
)
    }

验证功能

「实现完成后,我们试试效果」

「on=>emit,和之前一样,on 监听一个或多个事件,emit 触发该事件名下所有事件」

function funcA(args{
  console.log(args)
}
function funcB(args{
  console.log(++args.count);
}

messageCenter.on("a", funcA);
messageCenter.on("a", funcB);
messageCenter.emit("a", { count: 1 }); // { count: 1 }   2
messageCenter.emit("a", { count: 2 }); // { count: 2 }   3

「on=>emit=>un=>emit,on 监听事件,un 销毁事件且不再允许 emit 当前函数,若不传函数,则清除当前 type(事件名)下所有函数」

    messageCenter.on("a", funcB);
    messageCenter.emit("a", { count: 1 }); // 2
    messageCenter.un("a", funcB);
    messageCenter.emit("a", { count: 2 });

「once=>emit=>emit,once 监听一个或多个事件,emit 触发事件后立即销毁」

    messageCenter.once("a", funcB);
    messageCenter.emit("a", { count1 }); // 2
    messageCenter.emit("a", { count2 });

「on=>clear=>has,on 监听不同的事件,clear 重置当前事件列表」

    messageCenter.on("a", funcB);
    messageCenter.on("b", funcB);
    messageCenter.on("c", funcB);
    messageCenter.clear();
    console.info(
      messageCenter.has("a") || messageCenter.has("b") || messageCenter.has("c")
    ); // false
    console.info(messageCenter.events); // {}

「on=>handlerLength,on 在同一 type 中注册多个事件,handlerLength 返回事件数量」

    messageCenter.on("a", funcB);
    messageCenter.on("a", funcA);
    console.info(messageCenter.handlerLength('a')); // 2

「watch=>invoke,watch 注册事件,invoke 触发事件并等待结果」

const funcC = async (args) => {
  await syncFn();
  return args.reduce((pre, next) => (pre += next));
};
// 异步函数
const syncFn = () => {
  return new Promise((res) => {
    setTimeout(res, 1000);
  });
};
messageCenter.watch("c", funcC);
messageCenter.invoke("c", [123]).then((result) => {
    console.log(result); // 6
});

「写在最后」

「以上就是文章的所有内容了,如果对源码有兴趣的同学可以进入下面链接或者用 npm,pnpm 下载」

Gitee:MessageCenter: 基于发布订阅模式实现的一个事件消息中心[8]

「NPM:event-message-center – npm[9]

「源码参考:message-center.js – npm[10]

「感谢你看到了这里,对文章有任何问题欢迎互相学习讨论,如果文章有帮助到你,请给作者一个点赞作为鼓励吧,你的鼓励是作者创作的动力!」

Reference

[1]

前言: #%E5%89%8D%E8%A8%80

[2]

起步: #%E8%B5%B7%E6%AD%A5

[3]

功能设计: #%E5%8A%9F%E8%83%BD%E8%AE%BE%E8%AE%A1

[4]

工具函数实现: #%E5%B7%A5%E5%85%B7%E5%87%BD%E6%95%B0%E5%AE%9E%E7%8E%B0

[5]

消息中心类实现: #%E6%B6%88%E6%81%AF%E4%B8%AD%E5%BF%83%E7%B1%BB%E5%AE%9E%E7%8E%B0

[6]

验证功能: #%E9%AA%8C%E8%AF%81%E5%8A%9F%E8%83%BD

[7]

写在最后: #%E5%86%99%E5%9C%A8%E6%9C%80%E5%90%8E

[8]

MessageCenter: 基于发布订阅模式实现的一个事件消息中心: https://gitee.com/DieHunter/message-center

[9]

event-message-center – npm: https://www.npmjs.com/package/event-message-center

[10]

message-center.js – npm: https://www.npmjs.com/package/message-center.js


原文始发于微信公众号(阿宇的编程之旅):JS案例:基于发布订阅实现的事件消息中心-MessageCenter

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/247081.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!