宏微队列执行顺序

176 阅读2分钟

js是单线程

与JS的用途有关,js主要是用来实现用户与浏览器交互的浏览器脚本语言,如果是多线程,会带来很多复杂的同步问题,比如:不同的线程同时操作一个dom元素的问题。为了避免复杂性,将js设计为单线程
为了利用多核CPU的计算能力,h5提出Web Worker标准,允许JS脚本创建多个线程,但是子线程完全受主线程的控制,并且不能操作DOM。(因此这个标准并没有改变JS是单线程的本质)

任务队列

在任务队列中,分为两大类:同步任务、异步任务

执行栈

  1. 所有的同步任务都在主线程上执行,形成一个执行栈
  2. 主线程之外,还存在着一个“任务队列”,当异步任务有了结果,就在“任务队列”中放置一个事件
  3. 一旦“执行栈”中的所有同步任务执行完毕,系统回去读取“任务队列”,看其中的事件对应着的异步任务,会进入到执行栈开始执行。其中异步任务又分为了宏任务和微任务,宏任务和微任务再按照一定的规则执行
  • 主线程不断重复上面所述的三步,称为事件回滚(Event Loop)

宏队列

用来保存待执行的宏任务,如:定时器回调、DOM事件回调、ajax回调

微队列

用来保存待执行的微任务,如:promise回调、MutationObserver 回调、nextTick

js执行顺序

先执行所有初始化同步任务代码(可以看做是一个宏任务?),在执行的时候会挂起一些后执行的宏任务和微任务,执行完成后会执行被队列挂起的微任务,之后是宏任务(微任务的优先级高于宏任务)

setTimeout(() => { //立即放入宏队列
    console.log('timeout callback1()')
    Promise.resolve(3).then(
      value => { //立即放入微队列
        console.log('Promise onResolved3()', value)
      }
    )
  }, 0)
 
  setTimeout(() => { //立即放入宏队列
    console.log('timeout callback2()')
  }, 0)
 
  Promise.resolve(1).then(
    value => { //立即放入微队列
      console.log('Promise onResolved1()', value)
      setTimeout(() => {
        console.log('timeout callback3()', value)
      }, 0)
    }
  )
 
  Promise.resolve(2).then(
    value => { //立即放入微队列
      console.log('Promise onResolved2()', value)
    }
  )
  // Promise onResolved1() 1
  // Promise onResolved2() 2
  // timeout callback1()
  // Promise onResolved3() 3
  // timeout callback2()
  // timeout callback3() 1