消息队列
事件循环和消息队列

宏任务
- 渲染事件(如解析DOM、计算布局、绘制)
- ⽤⼾交互事件(如⿏标点击、滚动⻚⾯、放⼤缩⼩等)
- JavaScript脚本执⾏事件
- ⽹络请求完成、⽂件读写完成事件
消息队列中宏任务⼤致流程:
- 先从多个消息队列中选出⼀个最⽼的任务,这个任务称为oldestTask;
- 然后循环系统记录任务开始执⾏的时间,并把这个oldestTask设置为当前正在执⾏的任务;
- 当任务执⾏完成之后,删除当前正在执⾏的任务,并从对应的消息队列中删除掉这个oldestTask
- 最后统计执⾏完成的时⻓等信息。
微任务
第⼀种是把异步回调函数封装成⼀个宏任务,添加到消息队列尾部,当循环系统执⾏到该任务的时候执⾏回 调函数
第⼆种⽅式的执⾏时机是在主函数执⾏结束之后、当前宏任务结束之前执⾏回调函数,这通常都是以微任务 形式体现的。
微任务就是⼀个需要异步执⾏的函数,执⾏时机是在主函数执⾏结束之后、当前宏任务结束之前。
当JavaScript执⾏⼀段脚本的时候,V8会为其创建⼀个全局执⾏上下⽂,在创建全局执⾏上下⽂的 同时,V8引擎也会在内部创建⼀个微任务队列,微任务队列就是⽤来存放微任务的
微任务是怎么产⽣的?
第⼀种⽅式是使⽤MutationObserver监控某个DOM节点,然后再通过JavaScript来修改这个节点,或者为 这个节点添加、删除部分⼦节点,当DOM节点发⽣变化时,就会产⽣DOM变化记录的微任务。
第⼆种⽅式是使⽤Promise,当调⽤Promise.resolve()或者Promise.reject()的时候,也会产⽣微任务。
微任务队列是何时被执⾏
在当前宏任务中的JavaScript快执⾏完成时,JavaScript引擎会检查全局执⾏上下⽂中的微任务队列,然后按照顺序执⾏队列中的 微任务
结论:
- 微任务和宏任务是绑定的,每个宏任务在执⾏时,会创建⾃⼰的微任务队列
- 微任务的执⾏时⻓会影响到当前宏任务的时⻓。
- 在⼀个宏任务中,分别创建⼀个⽤于回调的宏任务和微任务,⽆论什么情况下,微任务都早于宏任务执 ⾏。
监听DOM变化⽅法演变
最开始使用setTimeout或者setInterval来定时检测DOM是否有改变。这种⽅ 式简单粗暴,但是会遇到两个问题:如果时间间隔设置过⻓,DOM 变化响应不够及时;反过来如果时间间 隔设置过短,⼜会浪费很多⽆⽤的⼯作量去检查DOM,会让⻚⾯变得低效。
后来采用观察者的设计模式,当DOM 有变动时就 会⽴刻触发相应的事件,这种⽅式属于同步回调。
再后来使⽤ MutationObserver,将响应函数改成异步调⽤,可以不⽤在每次DOM变化都触发异步调⽤,⽽是等多 次DOM变化后,⼀次触发异步调⽤
- 通过异步操作解决了同步操作的性能问题;
- 通过微任务解决了实时性的问题。