首页 > 其他分享 >[JS] 事件总线

[JS] 事件总线

时间:2023-11-25 14:35:40浏览次数:27  
标签:订阅 总线 eventObject JS eventName callback 事件 列表

事件总线与发布订阅模式

事件总线是对发布-订阅模式的一种实现。

发布-订阅模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

发布-订阅模式实现了松耦合,发布者不是直接将消息发送给订阅者,而是经过了一个中间的代理,事件总线就是一种中间代理的实现。

事件总线维护了一个事件列表,订阅者可以订阅某一个事件,并指定一个回调(回调的具体实现在订阅者内部);

每个事件又维护了一个依赖列表,发布者可以“触发”一个事件,事件总线负责遍历该事件的依赖列表,调用每一个当初订阅者订阅时指定的回调函数。

image-20231124223019810 image-20231124223036722

在 JS 中实现Event Bus

定义一个EventBus类:

class EventBus{}

需要维护一个事件列表,在初始化事件总线对象的时候创建。

对于每一个事件,我们需要记录它的事件名(string类型),还需要记录该事件的依赖列表(Array<Function>类型),依赖列表其实就是各个订阅者的回调函数的列表。

这采用了一个对象来记录多个事件,刚好键值对就是事件名:依赖列表

constructor(){
    this.eventObject = {};
}

实现订阅

每一次订阅需要指定订阅的事件名发布时要触发的回调函数

  • 如果指定的事件不存在,则添加一个事件,并推入该新依赖(回调函数)。
  • 如果指定的事件存在,则直接推入新依赖(回调函数)
subscribe(eventName, callback){
    if(!this.eventObject[eventName]){
        this.eventObject[eventName] = [];
    }
    this.eventObject[eventName].push(callback);
}

实现发布

发布也要考虑到指定的事件是否存在。如果不存在,则中断并返回警告;如果存在指定事件,则依次调用事件的依赖列表(回调列表)。

publish(eventName){
    const callbackList = this.eventObject[eventName];
    if(!callbackList)return console.warn(eventName + " Not Found!");
    for(let callback of callbackList){
        callback();
    }
}

汇总如下

class EventBus{
    constructor(){
        this.eventObject = {};
    }

    /**
     * @param {string} eventName 
    */
    publish(eventName){
        const callbackList = this.eventObject[eventName];

        if(!callbackList)return console.warn(eventName + " Not Found!");
        
        for(let callback of callbackList){
            callback();
        }
    }

    /**
     * @param {string} eventName 
     * @param {Function} callback 
    */
    subscribe(eventName, callback){
        if(!this.eventObject[eventName]){
            this.eventObject[eventName] = [];
        }

        this.eventObject[eventName].push(callback);
    }
}

优化Event Bus的实现

在发布时传递参数

使用...args介绍不定长参数列表,在发布时传入,并在调用回调函数列表的时候依次传入。

/**
* @param {string} eventName 
*/
publish(eventName, ...args){
    const callbackList = this.eventObject[eventName];

    if(!callbackList)return console.warn(eventName + " Not Found!");

    for(let callback of callbackList){
        callback(...args);
    }
}

提供取消订阅的操作

在订阅者调用subscribe方法订阅事件的时候,返回一个用于取消订阅的unSubscribe方法。

在实现事件的回调函数列表的时候,需要为每一个回调函数添加一个id,方便以后查询并删除该回调函数。

这里将订阅的回调函数列表换成用对象结构存储,因为在数组中删除某个中间元素较麻烦且耗时,效率不如对象结构的delete删除键值对。

换成对象结构存储后,键值对表示:id:回调函数

class EventBus{
    constructor(){
        // 初始化事件列表
        this.eventObject = {};
        // 回调函数列表的 id
        this.callbackId = 0;
    }

    /**
     * @param {string} eventName 
    */
    publish(eventName, ...args){
        // 取出该事件的回调函数列表(对象)
        const callbackObject = this.eventObject[eventName];

        if(!callbackObject)return console.warn(eventName + " Not Found!");
        
        // 执行每一个回调函数,这里的id是对象的key
        for(let id in callbackObject){
            callbackObject[id](...args);
        }
    }

    /**
     * @param {string} eventName 
     * @param {Function} callback 
     * @returns {{
     *      unSubscribe: Function
     * }}
    */
    subscribe(eventName, callback){
        if(!this.eventObject[eventName]){
            this.eventObject[eventName] = {};
        }
        // 为当前事务的回调函数申请一个专属id
        const id = this.callbackId++;
        // 绑定回调函数
        this.eventObject[eventName][id] = callback;
        // 生成取消订阅的函数
        const unSubscribe = ()=>{
            // 删除该回调函数
            delete this.eventObject[eventName][id];
            // 如果该事件的回调函数都删完了,则顺便删除事件列表中的事件
            if(Object.keys(this.eventObject[eventName]).length === 0){
                delete this.eventObject[eventName];
            }
        }
        
        return {unSubscribe};
    }
}

清除某个事件

/**
* @description 清除某事件
* @param {string} eventName
*/
clear(eventName){
    if(!this.eventObject.hasOwnProperty(eventName)){
        return console.warn(eventName + " Not Found!");
    }
    delete this.eventObject[eventName];
}

标签:订阅,总线,eventObject,JS,eventName,callback,事件,列表
From: https://www.cnblogs.com/feixianxing/p/js-event-bus-publish-subscribe-pattern.html

相关文章

  • 【SpringBoot应用篇】SpringBoot: 事件的发布和监听
    【SpringBoot应用篇】SpringBoot:事件的发布和监听应用场景概述自定义事件发布和监听pom自定义事件源和实体发布事件监听类使用ApplicationListener方式监听类使用@EventListener方式Spring事件最佳实践通用泛型类事件发布事件类事件监听类异步监听处理事......
  • JSON 格式的字符串转换回数组
    要将JSON格式的字符串转换回数组,你可以使用JavaScript的JSON.parse方法。这个方法可以将一个JSON字符串解析成JavaScript对象或数组。对于你的字符串,可以这样操作:假设你有一个JSON字符串str,其内容如下:'[{"goodsCode":"ABC1","qty":12.22},{"goodsCode":"ABC2","q......
  • Node.js安装及环境配置
    一.安装Node.js步骤1、下载对应你系统的Node.js版本:https://nodejs.org/zh-cn/2、选安装目录进行安装3、环境配置4、测试二、前期准备1、Node.js简介简单的说Node.js就是运行在服务端的JavaScript。Node.js是一个基于ChromeV8引擎的JavaScript运行环境。Node.js使用......
  • XRender watch事件
    watch监听watch 其实就是 onValuesChange(不提供对外使用)的增强版,用于监听表单数据改变,可以做到单字段细粒度的监听。语法特征:[path]:()=>{},path按照表单的数据结构路径书写就可以了,List组件的比较特殊,例如对应的表单字段是cityList需要写成 cityList[]。constwa......
  • 这篇保证你彻底搞懂Java NIO的Selector事件选择器
     Selector提供选择执行已经就绪的任务的能力,使得多元I/O成为可能,就绪选择和多元执行使得单线程能够有效率地同时管理多个I/Ochannel。C/C++许多年前就已经有select()和poll()这两个POSIX(可移植性操作系统接口)系统调用可供使用。许多os也提供相似的功能,但对Java程序......
  • 嵌入式使用quickjs
    零、前言之前搞过在嵌入式中引入Lua作为脚本,以实现动态执行效果。详见(https://www.cnblogs.com/wunaozai/p/14087370.html)但是众所周知原因,其实Lua远远没有JS好,一方面是目前前端的如日中天,加之前端开源的库很多。很多都可以复用。在选型用哪个JS引擎时,参考了网上的资料,......
  • quickjs入门学习
    由于最近在学习quickjs,把学习过程中遇到的问题和功能验证的过程都记录下来,这篇是quickjs入门学习的目录导航。  本文地址:https://www.cnblogs.com/wunaozai/p/17853962.html......
  • golang 根据 json path 提取字段值
    在Golang中,可以使用github.com/tidwall/gjson包来根据JSON路径提取字段值。 packagemainimport("fmt""github.com/tidwall/gjson")funcmain(){jsonStr:=`{"name":"John","age":30,......
  • uniapp 封装一个类似js-cookie可时效性存储token的方法
    贴代码cache.js/***存储数据*key:缓存的键名,必填*value:缓存的值,选填*seconds:缓存的过期时间,选填,单位为秒,默认为28天*/functionset(key,value,seconds){if(!key){//如果key为空,直接返回console.log("key不能空");return;}const......
  • 使用 pdf.js 在线预览 pdf 文件
    1、下载 https://github.com/mozilla/pdf.js/releases2、解压后得到build和web两个文件夹3、将其放入网站目录下4、使用web中的viewer.html即可在线预览pdf文件viewer.html?file=xxx.pdf5、无法加载请检查是否有相应的mime配置(mjs、ftl)......