浏览器
浏览器的构成
用户界面:地址栏,后退/前进按钮,书签目录等;(除了从服务器请求到的网页的窗口)
浏览器引擎:用来查询及操作渲染引擎的接口;
渲染引擎:用来显示请求的html内容;
网络:主要是来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作;
UI后端:用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口。
JS解释器 :用来解释执行JS代码;
数据存储:属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术;
浏览器的进程
Browser进程:浏览器的主进程,只有一个。主要有以下功能:
- 负责浏览器界面显示,与用户交互。如前进,后退等
- 负责各个页面的管理,创建和销毁其他进程
- 网络资源的管理,下载等
第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建;
GPU进程:最多一个,用于3D绘制;
浏览器渲染进程:默认每个Tab页面一个进程,互不影响,控制页面渲染,脚本执行,事件处理等(有时候会优化,如多个空白tab会合并成一个进程);
如图:
浏览器的渲染进程(浏览器内核)
通常说的浏览内核指的是浏览器的渲染引擎和js引擎
浏览器内核,即我们的渲染进程,我们页面的渲染,js的执行,事件的循环都在这一进程内进行。该进程下面拥有着多个线程,靠着这些线程共同完成渲染任务。这些线程如下:
1 图形用户界面GUI渲染线程
- 负责渲染浏览器界面,包括解析HTML、CSS、构建DOM树、Render树、布局与绘制等
- 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
2 JS引擎线程
- JS引擎,负责处理执行javascript脚本
- 等待任务队列的任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS引擎在运行JS程序
3 事件触发线程
- 这部分不属于JS引擎,其实归属于浏览器,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助);
- 当JS引擎执行代码块如setTimeout时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
- 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
- 注意:由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
4 定时触发器线程
setInterval
与setTimeout
所在线程- 定时计时器并不是由JS引擎计时的,因为如果JS引擎是单线程的,如果JS引擎处于堵塞状态,那会影响到计时的准确
- 当计时完成被触发,事件会被添加到事件队列,等待JS引擎空闲了执行
- 注意:W3C的HTML标准中规定,setTimeout中低与
4ms
的时间间隔算为4ms
5 异步HTTP请求线程
- 在XMLHttpRequest在连接后新启动的一个线程
- 线程如果检测到请求的状态变更,如果设置有回调函数,该线程会把回调函数添加到事件队列,同理,等待JS引擎空闲了执行
渲染进程(浏览器内核)的线程如下:
渲染进程中的线程之间的关系
GUI渲染线程与JS引擎线程互斥
因为JS引擎可以修改DOM树,那么如果JS引擎在执行修改了DOM结构的同时,GUI线程也在渲染页面,那么这样就会导致渲染线程获取的DOM的元素信息可能与JS引擎操作DOM后的结果不一致。为了防止这种现象,GUI线程与JS线程需要设计为互斥关系,当JS引擎执行的时候,GUI线程需要被冻结,但是GUI的渲染会被保存在一个队列当中,等待JS引擎空闲的时候执行渲染。
由此也可以推出,如果JS引擎正在进行CPU密集型计算,那么JS引擎将会阻塞,长时间不空闲,导致渲染进程一直不能执行渲染,页面就会看起来卡顿卡顿的,渲染不连贯,所以,要尽量避免JS执行时间过长。
JS引擎线程与事件触发线程、定时触发器线程、异步HTTP请求线程
事件触发线程、定时触发器线程、异步HTTP请求线程三个线程有一个共同点,那就是使用回调函数的形式,当满足了特定的条件,这些回调函数会被执行。这些回调函数被浏览器内核理解成事件,在浏览器内核中拥有一个事件队列,这三个线程当满足了内部特定的条件,会将这些回调函数添加到事件队列中,等待JS引擎空闲执行。例如异步HTTP请求线程,线程如果检测到请求的状态变更,如果设置有回调函数,回调函数会被添加事件队列中,等待JS引擎空闲了执行。
但是,JS引擎对事件队列(宏任务)与JS引擎内的任务(微任务)执行存在着先后循序,当每执行完一个事件队列的时间,JS引擎会检测内部是否有未执行的任务,如果有,将会优先执行(微任务)。
WebWorker
因为JS引擎是单线程的,当JS执行时间过长会页面阻塞,那么JS就真的对CPU密集型计算无能为力么?
所以,后来HTML5中支持了Web Worker
Web Workers 使得一个Web应用程序可以在与主执行线程分离的后台线程中运行一个脚本操作。这样做的好处是可以在一个单独的线程中执行费时的处理任务,从而允许主(通常是UI)线程运行而不被阻塞/放慢。
所以,如果需要进行一些高耗时的计算时,可以单独开启一个WebWorker线程,这样不管这个WebWorker子线程怎么密集计算、怎么阻塞,都不会影响JS引擎主线程,只需要等计算结束,将结果通过postMessage传输给主线程就可以了。
原文: