昨天阅读了一篇长文,很受启发。原文在此。
这篇文章好在顺序推进,层层深入讲解,从基础知识开始科普。中间又点名了很多细小的知识点,比如移动端滑动的时候计时器停止之类的情况。
而本篇博客是按照我的想法重新梳理一下该文章,在加上一些我自己的想法。
文章主要介绍了下面三件事情:
- 浏览器内的进程和线程的介绍
- 浏览器渲染流程
- event loop
后面两点我就不再赘述,大家可以通过原文进一步了解,主要说说第一点。
我们常说 js 运行是单线程的,有的同学会问,js 又是单线程又是事件驱动,如何做到?从这里我们延伸一下,其实浏览器本身是有多个进程和多个线程的。具体哪些进程和线程可以参照原文。我们口中的单线程其实是浏览器渲染进程中的 js 引擎线程,而包括事件处理线程、计时器线程、GUI 渲染线程,这些都并不归属于我们的那个单线程内。
看到 GUI 渲染线程,可能又有同学有疑问,渲染不是和 js 执行是互斥的吗?为什么在两个线程里?这里也是有一个误区:很多阅读材料会介绍因为 js 是单线程,所以渲染和执行 js 是互相阻塞的。但是实际上他们并不分配在一个线程,但是 js 引擎线程和 GUI 渲染线程本身是互斥的,至于为什么,原文也提到了:
由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起, GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。
文章还提到了 WebWorker,这是可以看作是解决 js 对 cpu 密集型计算的一种解决方案。就是在渲染进程在新开一个线程来解决。
还有注意 promise 的 polyfill 和原生的执行顺序不同……polyfill 是用 setTimeout实现的……也就是其实是在 macrotask 里而不是 microtask。详细的请看[原文]
再补一嘴在 es6 中 microtask 被称为 job,macrotask 称为 task。
(https://juejin.im/entry/590801780ce46300617c89b8?from=singlemessage&isappinstalled=0)吧~