发布订阅模式。
案例分析 (以制作一个简易播放器为例,是包括概念,不包括具体逻辑的实现)
播放器的主要步骤:
- 初始化播放器 -> 播放 -> 播放中 -> 结束
转化为代码: (demo1.js)
class Player{
init(){}
play(){}
stop(){}
}
这些步骤中有些步骤是非常耗时的,因此无法同步调用。比如说:初始化。(如果播放器初始化未完成时,是无法调用播放方法的)
转化为代码: (demo2.js)
// 由于每个步骤可能是非常耗时的操作。
class Player {
constructor(){
this.isInitSuccess = false;
}
init() {
setTimeout(() => { // 模仿耗时操作,假设两秒钟之后,播放器初始化完成
this.isInitSuccess = true;
}, 2000);
}
play() {
if (!this.isInitSuccess) throw Error('播放器为初始化')
}
stop() {}
}
let player = new Player()
// 代码会报错,因为init需要时间
player.init()
player.play()
这种问题怎么解决呢? 可以采用发布订阅模式; 转化为代码: (demo3.js)
class Player {
constructor() {
// initsuccess
this.events = {};
this.isInitSuccess = false;
}
init() {
setTimeout(() => { // 模仿耗时操作,假设两秒钟之后,播放器初始化完成
this.isInitSuccess = true;
this.emit('initsuccess','')
}, 2000);
}
...
on(eventNamve,cb){
this.events[eventNamve] = (this.events[eventNamve] ? this.events[eventNamve].push(cb) : [cb]);
},
emit(eventNamve,...args){
(this.events[eventNamve] || []).forEach(eventCallBack => {
eventCallBack(...args)
});
}
}
let player = new Player()
player.init();
player.on('initsuccess',()=>{
console.log('初始化完成')
})
什么叫发布订阅模式?
上述代码中,我们使用on注册一个函数,内部emit在适合的时机触发。我们把这种思路的代码,称之为 "发布订阅" 模式。 其中,触发on方法,我们称之为 '订阅者',触发emit,我们称之为 '发布者'。
案例扩展
- 增加了'播放中'与'播放完成'订阅者
转化为代码: (demo4.js)
class Player {
constructor() {
this.events = {};// initsuccess
this.isInitSuccess = false;
this.timer = null;
this.time = 10;
}
init() {
setTimeout(() => { // 模仿耗时操作,假设两秒钟之后,播放器初始化完成
this.isInitSuccess = true;
this.emit('initsuccess', '')
}, 2000);
}
play() {
if (!this.isInitSuccess) throw Error('播放器为初始化')
this.time = 10;
this.timer = setInterval(() => {
this.time--;
if (this.time===0){
clearInterval(this.timer)
this.emit('playiend','')
}
this.emit('playing', this.time)
}, 1000);
}
on(eventNamve, cb) {
this.events[eventNamve] = (this.events[eventNamve] ? this.events[eventNamve].push(cb) : [cb]);
}
emit(eventNamve, ...args) {
(this.events[eventNamve] || []).forEach(eventCallBack => eventCallBack(...args));
}
}
let player = new Player()
player.init();
player.on('initsuccess', () => {
console.log('初始化完成')
player.play()
});
player.on('playing', (time) => {
console.log('剩余播放时间',time)
})
player.on('playend', (time) => {
console.log('播放完成')
})
观察者模式
观察者模式是发布订阅模式一种简化的应用。
看案例
class Subject { // 被观察者的类 被观察者 需要将观察者收集起来
constructor(name) {
this.name = name;
this.state = '非常开心'
this.observers = [];
}
attach(o) { // 小宝宝 进行收集
this.observers.push(o); // on
}
setState(newState) {
this.state = newState;
this.observers.forEach(o => o.update(this.name, newState)) // emit
}
}
class Observer { // 观察者
constructor(name) {
this.name = name;
}
update(s, state) {
console.log(this.name + ":" + s + '当前' + state);
}
}
// vue 数据变了(状态) 视图要更新 (通知依赖的人)
let s = new Subject('小宝宝');
let o1 = new Observer('爸爸');
let o2 = new Observer('妈妈');
s.attach(o1)
s.attach(o2)
s.setState('不开心了')
特点
- 只有一个订阅事件(attach)。
- 增加状态的概念,案例中的:小宝宝的状态(开心,不开心)。
- 形成闭环概念。 (小宝宝不开心——> 通知爸爸妈妈 ---> 爸爸妈妈哄小宝宝)
发布订阅与观察者模式对比: