前面提到,浏览器的消息队列用来存储各个进程/线程向渲染主线程发送的事件,但因为有些事件的优先级是比较高的,而消息队列只能按顺序来无法满足优先级这一需求,为了满足优先级这个需求,于是微任务应运而生,微任务可以理解成是消息队列中的一种任务类型,消息队列中存放着来自若干个任务,这些任务都存在着各自的特点,于是按照一定的归类来讲各个任务分类。宏任务和微任务就是其中的两种任务类型。
宏任务和微任务
宏任务
宏任务包括但不限于
- 渲染事件
- 用户交互事件(比如鼠标点击)
- JS脚本执行事件
- 网络请求完成、文件读写完成事件
其实,一般来说消息队列里面的任务基本上都是宏任务,但由于宏任务的时间颗粒太大, 无法满足一些对于时间要求比较苛刻的任务,于是就有了对应的微任务来解决这一问题。
微任务
异步代码在JS引擎中的两种执行情况
- 把异步回调函数封装成一个宏任务,添加到消息队列尾部,当事件循环循环到该任务的时候,就会开始执行该回调函数
- 把异步回调函数封装成一个微任务,添加到微任务队列当中,当前宏任务结束之前、主函数执行结束之后开始执行该回调函数
微任务其实可以理解成就是一个需要异步执行的函数,在主函数执行结束之后、当前宏任务执行结束之前开始执行。浏览器通过事件循环机制来处理消息队列中的任务,那么微任务在事件循环机制中是怎样来执行的。
微任务的执行机制
-
当 JS 执行一段脚本时,V8会为其创建一个全局执行上下文,同时,还会专门创建一个微任务队列,但是这个微任务队列是私密的,即无法通过JS直接访问
-
当JS执行一些特殊操作的时候会产生一个微任务,JS引擎会使其加入到微任务队列中
- 当使用 MutationObserver 监控某个 DOM 节点,然后再通过JS来修改这个节点,或者增加、删除子节点,当DOM节点发生变化时,就会产生记录 DOM 变化的微任务
- 使用 Promise,当调用 Promise.resolve(),Promise.reject() 的时候,也会产生微任务
-
当JS脚本执行接近完成后,JS引擎会检查全局执行上下文中的微任务队列,然后按照顺序执行队列中的微任务,这个检查执行微任务的时间点一般被称为检查点
-
当检查到微任务队列中的微任务的时候,会按照顺序来依次完成微任务
-
等微任务队列清空后,JS引擎就会销毁整个全局执行上下文
从JS脚本执行的宏任务中的微任务分析,不难得出微任务的一些特性
- 每个宏任务在执行的时候,会创建自己的微任务队列,即一个宏任务绑定一个微任务队列
- 微任务的执行时长会影响到其所在的宏任务执行时长
- 在一个宏任务中,分别创建一个用于回调的宏任务和微任务,无论在什么情况下,微任务都早于宏任务