任务队列

首先我们需要明白以下几件事情:

* JS分为同步任务和异步任务
* 同步任务都在主线程上执行,形成一个执行栈
* 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
* 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。

根据规范,事件循环是通过任务队列的机制来进行协调的。一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合。**每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。** setTimeout/Promise 等API便是任务源,而进入任务队列的是他们指定的具体执行任务。

任务队列

javascript运行在单线程的环境下,许多异步方法的回调都会进入任务队列中,事件循环连有两种任务队列,宏任务队列和微任务队列

宏任务(macrotask)

  • setInterval
  • setTimeout
  • rquestAnimationFrame
  • setImmediate
  • I/O
  • UI rendering

微任务(microtask)

  • Promise
  • MutationObserver
  • Object.observe
  • process.nextTick(nodejs)

任务队列的执行顺序

  1. 每一次时事件循环,都是先执行同步代码后再执行任务队列里面的任务
  2. 任务队列和微任务队列是不同的队列,事件循环先执行微任务队列再执行宏任务队列
  3. 执行微任务队列的时候,微任务队列可以无限添加延长并执行,不需要等到下一次事件循环,直到微任务队列为空再执行宏任务队列

注意:如果微任务无限延长并执行,会导致宏任务被阻塞,在nodejs可以设置 process.maxTickDepth ,默认值是1000

MutationObserver

MutationObserver给开发者们提供了一种能在某个范围内的DOM树发生变化时作出适当反应的能力。该API设计用来替换掉在DOM3事件规范中引入的Mutation事件。可以是用此接口来验证上述的观点

点击outer元素时候,执行的顺序是

click -> promise -> mutate -> timeout

点击inner元素的时候,执行的顺序是

click -> promise -> mutate -> click -> promise -> mutate -> timeout -> timeout

扩展阅读

理解 JavaScript 中的 macrotask 和 microtask
通过microtasks和macrotasks看JavaScript异步任务执行顺序
setImmediate.js
macrotask与microtask
JavaScript 异步、栈、事件循环、任务队列