发布-订阅模式

123 阅读2分钟
基本定义

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

订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。

实现思路
  • 初始化一个空对象作为事件调度中心 events = {},以 "事件" 为 key,事件发生后执行的方法为任务队列(数组)。
  • 添加事件注册 on(event,callback)方法 , 判断如果事件调度中心(events)中存在该事件,直接将事件回调函数 push 至任务队列, 如果没有则以数组的形式增加该回调函数。
  • 事件移除 off(event,callback) 方法, 找到该 "事件" event 的任务队列,如果数组存在 (events[event]),如果 callback存在,查找 callback 的下标 index, 删除该回调函数;如果 callback 不存在, 删除该事件所以任务队列(events[event] = []).
  • 添加事件发布(触发) fire(event) 方法, 如果该 event 的任务队列存在,遍历数组(任务队列)中的 callback,并执行
  • 单次触发事件:内部调用 fire 方法,执行之后删除此事件队列。
代码实现
    class Event {
        constructor () {
            this.events = {}
        }
        
        // 注册事件
        on (event, callback) {
            if(!this.events[event]) this.events[event] = []
            this.events[event].push(callback)
        }

        // 一次通知
        once () {
            let event = Array.prototype.shift.call(arguments)
            this.fire(event, ...arguments)
            delete this.events[event]
        }

        // 发布通知
        fire () {
            let event = Array.prototype.shift.call(arguments)
            let callbacks = this.events[event]
            if(!callbacks || !callbacks.length) return 
            callbacks.forEach(callback => {
                callback.apply(this,arguments)
            });
            
        }

        // 销毁监听
        off (event, callback) {
            let callbacks = this.events[event]
            if (!callbacks) return false
            if(!callback) return callbacks && (callbacks = [])
            for(let i = callbacks.length-1; i>=0; i--){
                let _cb = callbacks[i]
                if ( _cb === callback) {
                    callbacks.splice(i,1)
                }
            }
            
        }
    }
    
    // demo 测试
    const event = new Event()

    event.on("test",(index)=>{
        console.log("test", index)
    })
    event.on("test",(index)=>{
        console.log("test two", index)
    })
    event.fire("test",1)
    event.fire("test",2)
    event.once("test",3)
    event.once("test",4)