订阅发布模式:
这是一种广泛应用于异步编程的模式,是回调函数的事件化,常常用来解耦业务逻辑。事件的发布者无需关注订阅的侦听器如何实现业务逻辑,甚至不用关注有多少个侦听器存在。数据通过消息的方式可以灵活的传递。 ——《深入浅出Nodejs》
由于浏览器js单线程的原因,编写js代码经常会用到异步,回调函数 , 业内解决 异步比较流行的方式 订阅发布模式、 promise 、async/await 。
例如 promise 我们都是在then 函数中传入回调函数, 传入之后发生了什么。。。为什么回调没有立即执行 而是等状态发生后执行的,同一个promise 实例 注册多个then函数, 为什么可以依次执行。。。很多主流的库或框架 也用的订阅发布模式,例如 mobx vue . 所以有必要 总结下原理,相信你看完本文, 收获颇多。
下面我们可以构造下事件类 需要包含下面的 内容:subscribers
:一个对象,其中每个属性(订阅的事件类型),对应一个数组,存储订阅者(同一个事件类型可能有多个订阅者,故放到数组中保存)subscribe()
:注册/订阅,将订阅者添加到 subscribers 数组中;unsubscribe()
:取消订阅。从 subscribers 数组中删除订阅者;publish()
:循环遍历 subscribers 数组中的每一个元素,并且调用它们注册时所提供的方法;
所有这三种方法都需要一个 type 参数。这是因为发布者可能触发多个事件(比如同时发布一本杂志和一份报纸)
class Publisher {
constructor(){
this.subscribers={}
}
subscribe(type,fn){
// 将订阅的事件回调保存到
if (typeof fn !=='function'){
throw new Error (`${fn} is not a function`)
}
if(!this.subscribers[type]){
this.subscribers[type] = []
}
this.subscribers[type].push(fn);
}
unsubscribe(type,fn){
this.subscribers[type]=this.subscribers[type].filter(item=>item!==fn)
}
publish(type,...params){
if(!this.subscribers[type])return;
this.subscribers[type].forEach(item=>item(...params));
}
static makePublisher (obj){
obj.publisher = new Publisher(); // 创建一个发布者
}
}
function fn1(name){
console.log(`hello,my name is ${name}`);
}
function meet(){
console.log('nice to meet you ');
}
function fn2 (age){
console.log(`hello , I am ${age} years old!`);
}
var people= {};
Publisher.makePublisher(people);
people.publisher.subscribe('name',fn1);
people.publisher.subscribe('name',meet);
people.publisher.unsubscribe('name',meet);
people.publisher.subscribe('age',fn2);
people.publisher.publish('name','Tom');
people.publisher.publish('age',30);
//hello,my name is Tom
// hello , I am 30 years old!