Event Loop 、JS 引擎、渲染引擎之间的合作(未完成)

276 阅读4分钟

渲染流程会做什么?

为什么需要eventLoop?

js引擎的编译流水线是什么?

JS宿主环境可能会有哪些不同?

requestAnimationFrame是宏任务还是微任务?

micro task 和 check解决了什么样的问题?

JS引擎的组成

js引擎包括parser、解释器、gc再加上JIT编译器

parse: 负责将源码转成AST

interperter: 解释器,负责转换AST成字节码,并且解释执行

JIT compiler: 对执行时的热点函数进行收集然后编译,把字节码转换成机器码,之后就可以直接执行机器码

gc(garbage collector): 垃圾回收器, 清理堆内存中不再使用的对象。

编译流水线

一般的 JS 引擎的编译流水线是 parse 源码成 AST,之后 AST 转为字节码,解释执行字节码。运行时会收集函数执行的频率,对于到达了一定阈值的热点代码,会把对应的字节码转成机器码(JIT),然后直接执行。这就是 js 代码能够生效的流程。

24c2678e7ffa4239ba55ec02b4bbc1a9_tplv-k3u1fbpfcp-watermark.jpg

渲染引擎流程做了什么??

渲染时会把 html、css 分别用 parser 解析成 dom 和 cssom,然后合并到一起,并计算布局样式成绝对的坐标,生成渲染树,之后把渲染树的内容复制到显存就可以由显卡来完成渲染。

abd0e8d5431a451fad4287042a1e6b22_tplv-k3u1fbpfcp-watermark.jpg

JS引擎和渲染引擎如何配合呢??

JS 引擎只会不断执行 JS 代码,渲染引擎也是只会布局和渲染。要怎么综合两者呢?

两种思路

多线程

分为多个线程,主线程用来操作 ui 和渲染,其他线程用来执行一些任务(不能多个线程同时修改 ui,顺序没法控制)。

单线程

js最开始的设计知识用来做表单处理,就没有采用多线程架构,而且js设置为单线程的原因是为了防止DOM冲突的问题,那么需要将js执行和ui渲染还有一些需要异步来处理的事情就需要一个机制来实现,浏览器对这个机制的实现方案就是Event Loop。

不同的宿主环境中对Event Loop的实现是不同的:

1.浏览器主要是调度渲染和js各项任务的执行,还有worker

2.node里面主要是调度各种io

浏览器的Event Loop

渲染,js, worker的调度问题

每次loop会执行js一个宏任务,然后执行所有现有的微任务,然后需要check是不是需要渲染,然后会check一下是不是需要处理worker的信息。微任务就是js中需要及时办的任务,队列中要是有了微任务到有时间解决了就需要一次性的全部解决掉。

ccdb262565814ad0b679af19cf983104_tplv-k3u1fbpfcp-watermark.jpg

js计算与渲染的冲突问题

每一帧的计算和渲染是有固定频率的,如果 JS 执行时间过长,超过了一帧的刷新时间,那么就会导致渲染延迟,甚至掉帧(因为上一帧的数据还没渲染到界面就被覆盖成新的数据了),给用户的感受就是“界面卡了”。

Event Loop的每一个阶段都有可能会发生因为js计算的掉帧问题,那我们就要控制js的计算量,除此之外浏览器也提供了API让我们能够将计算做拆分来执行,合理的调度api就会使页面渲染效果变好,有requestAnimationFrame和requestIdleCallback两个API

渲染之前的一个回调函数(requestAnimationFrame)

requestAnimationFrame 是每次 loop 结束发现需要渲染,在渲染之前执行的一个回调函数,不是宏微任务。

看时机而执行的API(requestIdleCallback)

requestIdleCallback 会在每次 check 结束发现距离下一帧的刷新还有时间,就执行一下这个。如果时间不够,就下一帧再说。那如果一直没时间执行呢?那就需要强制执行了,也就是说把需要进行计算的代码放到这个API里面一直得不到执行,那影响了计算的进行也不合理,所以提供了 timeout 的参数可以指定最长的等待时间,如果一直没时间执行这个逻辑,那么就算拖延了帧渲染也要执行。

93dc8465f5304cf8977fe005d6f0ef15_tplv-k3u1fbpfcp-watermark.jpg

总结

event loop 是宿主环境为了集合渲染和 JS 执行,也为了处理 JS 执行时的高优先级任务而设计的机制。 loop 内的逻辑执行不能阻塞 check,也就是不能阻塞渲染引擎做帧刷新。所以不管是 JS 代码宏微任务、 requestAnimationCallback、requestIdleCallback 都不能计算时间太长。这个问题是前端开发需要考虑的问题。