为什么要设计微任务
js设计微任务,是为了解决宏任务的执行时机不可控的问题。如果只有宏任务,那任务队列里如果有一些紧急的任务,但是他前面还有别的耗时的回调的话,就比较麻烦。
有了微任务队列,因为主线程同步代码执行完毕以后,会优先执行微任务,所以如果比较紧急的任务,就放到微任务队列里就好了。
如果你看过vue的文档,就会发现,vue在更新dom的时候就是异步的。如果侦听到数据变化,vue就会开启一个队列。vue在内部执行就是放到了微任务里,他会尝试使用Promise.then,MutationObserver,setImmediate,如果执行环境不支持,才会采用宏任务setTimeout(fn,0)代替。
浏览器是多线程的
一个浏览器进程,是有多个线程的。比如,渲染引擎线程,js引擎线程,浏览器事件线程,http异步线程,时间队列轮询线程,定时器触发线程等。其中,js引擎线程就是解析和执行js的同步代码。如果遇到了异步任务,这个任务会被挂起,根据不同的类型(比如http请求,定时器异步),交给不同的线程去处理。等处理完以后,再把回调推入到事件队列里。
发布订阅模式
js用回调解决异步任务简单方便,但是会形成回调地狱,不利于代码的维护。针对这个情况,出现了很多解决方案,今天先学习了解发布订阅模式解决回调地狱问题。
代码实现发布订阅模式
class Publisher {
constructor() {
this.subsMap = {}
}
// 消息订阅
subscribe(type, cb) {
if (this.subsMap[type]) {
if (!this.subsMap[type].includes(cb)){
this.subsMap[type].push(cb)
}
} else {
this.subsMap[type] = [cb]
}
}
// 消息退订
unsubscribe(type, cb) {
if (!this.subsMap[type] ||
!this.subsMap[type].includes(cb)) {
return false
}
const idx = this.subsMap[type].indexOf(cb)
this.subsMap[type].splice(idx, 1)
}
// 消息发布
notify(type, ...payload) {
if (!this.subsMap[type]) {
return false
}
this.subsMap[type].forEach(cb => cb(...payload))
}
}
const ayu = new Publisher()
ayu.subscribe('javascript', msg => console.log('javascript' + msg))
ayu.notify('javascript','开始学习')