任务队列
首先我们需要明白以下几件事情:
* 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)
任务队列的执行顺序
- 每一次时事件循环,都是先执行同步代码后再执行任务队列里面的任务
- 任务队列和微任务队列是不同的队列,事件循环先执行微任务队列再执行宏任务队列
- 执行微任务队列的时候,微任务队列可以无限添加延长并执行,不需要等到下一次事件循环,直到微任务队列为空再执行宏任务队列
注意:如果微任务无限延长并执行,会导致宏任务被阻塞,在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 异步、栈、事件循环、任务队列