这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
- 理解什么是观察者模式
- 理解什么是发布订阅模式
- 理解什么是
queueMicrotask()
本篇是Promise
的基本前置知识,让手写一个Promise
变的不在困难!!!,这是构建Promise
的0
1. 观察者模式
1.1 基本理解
如图【观察者模式简图】:
观察者模式讲述的是一对多的关系,简单的来说就是一个对象的状态改变,触发不同观察者动作回应,在《图解设计模式》一书当中,观察者模式就是做状态管理的一种
按图,我们开始实现一个观察者模式
1.2 实现一个简单的观察者模式示例
- 创建一个
Observer
的类 - 创建一个被观察的类
- 被观察的类需要添加一个方法
addObserver
,来收集实例化的Observer
- 状态改变,通知实例化的
Observer
// 创建一个Observer的类
class Observer {
constructor(name,action = ()=>{}) {
this.name = name;
this.action = action
}
runAction(state){
this.action(state,this.name)
}
}
// 创建一个被观察的类
class Subject {
constructor(name,state) {
this.name = name;
this.nowState = state;
this.oldState = null;
this.obseverList = new Set();
}
// 被观察的类需要添加一个方法addObserver,来收集实例化的Observer
addObserve(obsever){
this.obseverList.add(obsever)
return this
}
// 状态改变,通知实例化的Observer
notifyObserve(state){
this.nowState = state;
if(this.oldState!==this.nowState){
for(let item of this.obseverList){
item.runAction(this.nowState)
}
this.oldState = this.nowState
}
return this;
}
}
const teacherA = new Observer('老师A',(state,name)=>{
if(state === 'playing'){
console.log( name+'发现你在玩,打电话找你家长' )
}else if( state === 'studying'){
console.log( name+'狠狠的表扬了你' )
}
})
const teacherB = new Observer('老师B', (state,name)=>{
if(state === 'playing'){
console.log( name+'发现你在玩,罚站30分钟' )
}
})
const deskmateA = new Observer('小明',(state,name)=>{
if(state === 'eating'){
console.log( name+'发现你在吃东西,因此他也开始吃' )
}
})
const deskmateB = new Observer('小f',(state,name)=>{
if(state === 'eating'){
console.log( name+'发现你在吃东西,因此他也开始吃' )
}
})
const You = new Subject('你','studying')
You.addObserve(deskmateA).addObserve(teacherA).addObserve(teacherB)
You.notifyObserve('playing')
You.notifyObserve('eating')
You.notifyObserve('studying')
上面就是一个非常简单的观察者模式的实现。
从中可以清楚的看到,Subject
和各个Observer
是松耦合关系。当state
改变的时候,统一调取各个实例化Observer
的runAction
去根据状态执行相应的逻辑就好了。
2 发布订阅模式
2.1 基本理解
发布订阅模式和观察者模式,大体上很像(比如尤大大就认为发布订阅模式和观察者模式就是一样的)。这里就不讨论大佬的思想,毕竟我们都还不到掌控代码的程度。不过可以用观察者模式去类比发布订阅模式(发布订阅模式里的Publisher
,就是观察者模式里的Subject
,而Subscriber
,就是Observer
),只不过在发布订阅模式里面多了一个第三方Observer
来转发,而不是Subscriber
直接notify
状态给Publisher
在前端中,其实浏览器监听事件就是一个典型的发布订阅模式。例如,document.addEventListener('click',()=>{})
, document
就是被监听也就是Publisher
,'click'代表发布内容Subject
,后面的函数的意思就是,订阅到这个事件是click
那就执行这个函数。顺着这个来,就能出图了
如图【发布订阅模式】:
2.2 实现一个简单的发布订阅模式示例
- 创建一个第三方观察者
Observer
类 - 给第三方的观察者一个
addEventListener
来添加订阅某消息和订阅该消息的Subscriber
行为,只是这里要注意的是,同一个消息不止一个Subscriber
的行为可以定,继续添加Subscriber
的行为 - 给第三方的观察者一个
removeEventListener
可以订阅解除,解除分两种一种是解除单个Subscriber
对某个消息的订阅,和直接解除对某个消息的订阅 - 给第三方的观察者一个
notify
方法接受到Publisher
的状态,触发Subscriber
行为
class Observe {
constructor() {
this.publisherMap = new Map();
}
/*
* @param { String } type 订阅消息的类型
* @param { Function } actionfn 相应的Subscriber的动作,也是推送消息的目标的动作(Subscriber)
**/
addEventListener(type,actionfn){
if( !actionfn ){
console.error('绑定失败')
}
let actionfnList = new Set();
if(this.publisherMap.has(type)){
actionfnList = this.publisherMap.get(type)
}
actionfnList.add(actionfn)
this.publisherMap.set(type,actionfnList)
}
// 取消订阅 Publisher
removeEventListener(type,actionfn){
if( this.publisherMap.has(type) && actionfn){
const actionfnList = this.publisherMap.get(type)
if(actionfnList.size){
actionfnList.delete(actionfn)
}else{
this.publisherMap.delete(type)
}
}else if( this.publisherMap.has(type) && !actionfn){
this.publisherMap.delete(type)
}else{
console.error('暂无这个消息队列')
}
}
// 触发 Subscriber 的动作,也叫发布通知消息
notify(type){
if(this.publisherMap.has(type)){
const actionfnList = this.publisherMap.get(type)
for(let subscriberAction of actionfnList){
subscriberAction(this);
}
}
}
}
// 老师任命班长来监听你的状态
const monitor = new Observe()
const yourState = ['studying','eating']
const teacherAction = function(){
console.log('要很很的表扬你')
}
const deskmeatAction = function(e){
console.log('一起吃东西',e)
}
monitor.addEventListener(yourState[0],teacherAction)
monitor.addEventListener(yourState[0],()=>{
console.log('给你一朵大大的红花')
})
monitor.addEventListener(yourState[1],deskmeatAction)
monitor.notify(yourState[0])
monitor.notify(yourState[1])
从中可以发现,发布订阅模式里,发布者
和订阅者
,不是松耦合,而是完全解耦的。他们之间的联系由第三方的Observer
来协调。从代码实现上来看,必要的构造函数只实现第三方的Observer
即可
从中也能发现,发布订阅模式和观察者模式最大的区别在于,在发布订阅模式里,发布者,并不会直接通知订阅者,换句话说,发布者和订阅者,彼此互不相识。这也是完全解耦的特点
3. 你所不知道的queueMicrotask()
在JavaScript
中通过 queueMicrotask()
使用微任务
这里不对微任务宏任务做深入研究,只简单说明一下:任务可以看成一条一条的执行语句。,一般情况下是同步运行的,从上而下,当遇到异步的事件,就需要将异步事件推入事件循环队列,而事件循环队列有两种,一种是微任务循环队列,一种是宏任务循环队列。微任务先于宏任务执行。
简单使用如下:
console.log('a')
window.queueMicrotask(()=>{
console.log('b')
})
console.log('c')
掌握上面的内容,手写Promise
将变的异常轻松。完全不需要死记硬背了!!!
参考资料
- 观察者模式 vs 发布订阅模式h
- 图解设计模式
- queueMicrotask