17 微任务和宏任务

Posted by CodingWithAlice on April 13, 2021

17 微任务和宏任务

微任务MutationObserverPromise以及以Promise为基础的技术

宏任务渲染事件(解析DOM、计算布局、绘制)、用户交互事件(鼠标点击、滚动页面、放大缩小等)、js脚本执行事件网络请求完成setTimeout()setInterval

复习宏任务定义

  • 主线程中,引入了消息队列(包括延迟执行队列和普通消息队列)和事件循环机制,主线程采用一个for循环,从消息队列里面取出任务并执行。

其中涉及的事件循环机制流程:

1、先从多个消息队列中选出一个最老的任务,这个任务称为 oldestTask

2、循环系统记录任务开始执行的时间,并把 oldestTask 设置为当前正在执行的任务

3、当任务执行完成之后,删除当前正在执行的任务,并从对应的消息队列中删除掉 oldestTask

4、最后统计执行完成的时长等信息

宏任务缺陷 - 时间粒度大

对时间精度要求较高的需求(如DOM变化的事件),宏任务就难以胜任,原因如下:

  • 任务随时有可能被添加到消息队列中(系统级的任务等),添加事件是由系统操作的,js不能准确掌控任务添加的位置,即不能控制任务开始的时间:例如setTimeout回调

什么是微任务

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

V8引擎怎么实现微任务

JS 执行一段脚本的时候,V8 会为其创建一个 全局执行上下文 ,在创建全局执行上下文的同时,V8 引擎也会在内部创建一个 微任务队列 –> 每个宏任务都关联了一个微任务

微任务是怎么产生的

两种方式:

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

  • 第二种方式是使用 Promise,当调用 Promise.resolve() 或者 Promise.reject() 的时候,也会产生微任务。

微任务是什么时机执行的

  • 检查点

在当前宏任务中的 JavaScript 快执行完成时,也就在 JavaScript 引擎准备退出全局执行上下文并清空调用栈的时候,JavaScript 引擎会检查 全局执行上下文中的微任务队列,然后按照顺序执行队列中的微任务

  • 注意

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

—> 也就是说在 执行微任务过程中产生的新的微任务并不会推迟到下个宏任务中执行,而是在当前的宏任务中继续执行。

image-20210413202144497

微任务知识点总结

由上述分析可知:

1、微任务和宏任务是绑定的,每个宏任务在执行时都会创建自己的微任务

2、微任务的执行时长 会影响到当前宏任务的执行时长

3、在一个宏任务中分别创建一个用于回调的宏任务和微任务,微任务永远早于宏任务执行

了解一下 MutationObserver

  • 一开始的DOM监听是通过 js轮询 查询是否有变化 –> 不能兼顾效能和实时性
  • 然后是 Mutation Event,当 DOM 有变动时就会立刻触发相应的事件,这种方式属于 同步回调 –> 这种实时性造成了严重的性能问题
  • 现在是 MutationObserver 将响应函数改成异步调用,可以不用在每次 DOM 变化都触发异步调用,而是等 多次 DOM 变化后,一次触发异步调用,并且还会使用一个数据结构来记录这期间所有的 DOM 变化 –> 这些记录被封装成微任务

总结而言,MutationObserver 采用了异步 + 微任务的策略。

  • 通过异步操作解决了同步操作的性能问题
  • 通过微任务解决了实时性的问题