丁鹿学堂:js中异步任务原理总结(二)

73 阅读2分钟
为什么要设计微任务

js设计微任务,是为了解决宏任务的执行时机不可控的问题。如果只有宏任务,那任务队列里如果有一些紧急的任务,但是他前面还有别的耗时的回调的话,就比较麻烦。

有了微任务队列,因为主线程同步代码执行完毕以后,会优先执行微任务,所以如果比较紧急的任务,就放到微任务队列里就好了。

如果你看过vue的文档,就会发现,vue在更新dom的时候就是异步的。如果侦听到数据变化,vue就会开启一个队列。vue在内部执行就是放到了微任务里,他会尝试使用Promise.then,MutationObserver,setImmediate,如果执行环境不支持,才会采用宏任务setTimeout(fn,0)代替。

浏览器是多线程的

一个浏览器进程,是有多个线程的。比如,渲染引擎线程,js引擎线程,浏览器事件线程,http异步线程,时间队列轮询线程,定时器触发线程等。其中,js引擎线程就是解析和执行js的同步代码。如果遇到了异步任务,这个任务会被挂起,根据不同的类型(比如http请求,定时器异步),交给不同的线程去处理。等处理完以后,再把回调推入到事件队列里。

发布订阅模式

js用回调解决异步任务简单方便,但是会形成回调地狱,不利于代码的维护。针对这个情况,出现了很多解决方案,今天先学习了解发布订阅模式解决回调地狱问题。

11.png

代码实现发布订阅模式
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','开始学习')