# 宏任务/微任务

随着浏览器的应用领域的广泛,消息队列中这种粗时间颗粒度的任务已经不能够胜任部分领域的需求了。所以出现了微任务,微任务可以在实时性和效率之间做一个有效的权衡。

# 宏任务

  • 渲染事件 (解析DOM、计算布局、绘制)。
  • 用户交互事件(鼠标点击、滚动页面、放大缩小)。
  • js 脚本执行事件。
  • 网络请求完成、文件读写事件。
  1. 先从多个消息队列中选出一个最老的任务,这个任务称为 oldestTask.
  2. 然后循环系统记录任务开始执行的时间,并把这个 oldestTask 设置为当前正拶起执行的任务。
  3. 当任务执行完成之后,删除当前正在执行的任务,并从对应的消息队列中删除掉这个 oldestTask.
  4. 最后统计执行完成的时长等信息。

# 出现的问题

js 代码不能精准控制任务要添加到队列中的位置,控制不了任务在消息队列中的位置,很难控制开始执行任务的时间。

但是如果是两个连续的宏任务,需要连续执行,但是中间可能被渲染引擎插在两个任务中间其他的任务。

宏任务的时间粒度较大,执行的时间间隔是不能精准控制的,对一些实时性高的需求不符合。

# 微任务

微任务就是一个需要异步执行的函数,执行时机在主函数执行结束之后、当前宏任务结束之前。

V8 在创建全局上下文的同时,也会在内部创建一个微任务队列。每个红任务都关联了一个微任务队列。

  1. 微任务的产生时机
  • 使用 MutationObserver 监控某个 DOM 节点,然后再通过 js 来修改这个节点,或者为这个节点添加、删除部分子节点。当 DOM 节点发布变化时,就会产生DOM变化记录的微任务.

  • 使用 Promise, 当调用 Promise.resolve 或 Promise.reject 的时候,也会产生微任务。

  1. 微任务被执行
  • 在当前宏任务中的 js 快执行完时,js 引擎会检查全局执行上下文中的微任务队列,然后按照顺序执行队列中的微任务。这个时间点称为检查点。

  • 在执行微任务的过程中,产生了新的微任务,同样会将该微任务添加到微任务队列中,V8 引擎一直循环执行微任务队列中的任务,直到队列空才结束。

# 宏任务和微任务

  • 微任务和宏任务是绑定的,每个宏任务在执行时,都会创建自己的微任务。
  • 微任务的执行时长会影响到当前宏任务的时长。
  • 在一个宏任务重,无论什么情况下,微任务都是早于宏任务的。

# 监听 DOM 变化演变

MutationObserver 是用来监听 DOM 变化的一套方法。

可以利用 HTML 与 js 构建其自定义控件, 这些控件必须能够适应内容更改,响应事件和用户交互。web 应用需要监视 DOM 变为并及时作出响应。

  1. 早期页面并没有提供队监听的支持。要观察 DOM 是否变化,唯一能做的就是轮询检测,比如使用 setTimeout 或者 setInterval 来定时的检测 DOM 是否变化。这种方式简单粗暴。 但是要考虑时间间隔设置。及时性和低效性。

  2. 2000 年引入 Mutation Event ,采用观察者设计模式,当 DOM 有变化就立即出发相应的事件。解决了实时性,因为 DOM 一旦发生变化,就会调用 js 接口。造成了性能问题。阻塞页面,导致动画的卡顿。

因为使用 Mutation Event 会导致页面性能问题,所以被反对使用了。

  1. DOM4 开始,推荐使用 MutationObserver 来代替 Mutation Event. MutationObserver API 可以用来监视 DOM 变化,包括属性的变化、节点的增减、内容的变化。

  2. MutationObserver 将响应函数改成异步调用,可以不用在每次 DOM 变化都触发异步调用,而是等多次 DOM 变化后,一次触发异步调用,并且还会使用一个数据结构来记录这期间所有的 DOM 变化。这样即使频繁的操作 DOM,也不会对性能造成太大的影响。

  3. 对于实时性来说,在每次 DOM 节点变化的时候,渲染引擎将变化记录封装成微任务,并将微任务添加进当前的微任务队列,当执行到检查点的时候,V8引擎就会按照顺序执行微任务了。