浏览器是多进程的
- 浏览器之所以能运行,是因为系统给它的进程分配了资源(cpu、内存)
- 每开一个标签页,就相当于创建了一个独立的浏览器进程。(有的时候会将多个标签页合并成一个进程)
浏览器包含哪些进程
1、Broswer进程(主线程):
- 负责浏览器界面的显示、用户交互(如前进、后退)
- 负责各个页面的管理,创建和销毁其他进程
- 负责将渲染进程得到的内存中的Bitmap(位图)绘制到用户界面上
- 网络资源的管理、下载等
2、第三方插件进程:
- 每种类型的插件对应一个进程,仅当使用该插件时才创建
3、GPU进程:
- 最多一个,用于3D绘制等
4、浏览器渲染进程(浏览器内核):
- 该进程内部是多线程
- 每个tab页面一个进程,互不影响(打开浏览器时,就会看到两个进程:主进程+Tab页的渲染进程)
- 主要负责页面渲染、脚本执行、时间处理
浏览器渲染进程 -- 浏览器内核
多线程:
- GUI渲染线程
- 负责渲染页面,解析HTML、CSS文件,构建DOM树和CSS树,将DOM树和CSS树合并成render树
- 将渲染结果:位图交给主进程(Broswer进程)显示
- 当界面需要重绘(repaint)或某种操作导致回流(reflow)时,该线程就会执行。
- GUI渲染线程与JS引擎线程是互斥的,当JS引擎线程执行时,该线程就会被挂起,GUI更新会保存在一个队列中,等JS引擎空闲时再执行。
- JavaScript引擎线程(主线程)
- 也成为JS内核,负责解析处理js脚本,运行代码。(例如V8引擎)
- 一个Tab页无论什么时候都只有一个JS线程在运行JS程序。
- 事件触发线程
- 归属于浏览器而不是JS引擎,用来控制事件循环(因为JS引擎自己都忙不过来,需要浏览器另开线程辅助)
- 当有事件时,就会添加到事件线程中,比如当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
- 管理任务队列:当满足触发条件时,事件触发线程就会将事件监听函数放置到任务队列的队尾,等待主线程空闲时调用。
- 定时触发器线程
- 传说中的setInterval与setTimeout所在线程
- 负责计时,计时完毕后,将事件监听函数添加到任务队列里
- 因为JS引擎是单线程的,如果由JS引擎计时,有可能JS引擎发生堵塞,导致计时不准确
- 异步HTTP请求线程
- 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
- 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。
浏览器内核中线程之间的关系
- GUI渲染线程与JS引擎线程互斥
- JS阻塞页面加载
- 从互斥的关系就可推导出,如果JS执行时间太长就会阻塞页面的渲染
浏览器渲染流程
如何解决JS单线程导致的问题
当JS执行时间过长会阻塞页面,所以后来HTML5中支持了WebWorker
- WebWorker
- 当有非常耗时的工作时,比如计算等,就可以创建一个Worker线程
- 用构造函数创建Worker线程,JS引擎会向浏览器申请开一个子线程,受主线程控制,不能操作DOM
- Worker线程相当于浏览器为JS引擎开设的外挂,专门用来解决那些大量计算问题
- JS主线程与Worker线程的通信是通过postMessage API,需要通过序列化对象来与线程交互特定的数据。当计算出结果后,将结果通信给主线程即可。
<!DOCTYPE html>
<html>
<body>
<p>Count numbers: <output id="result"></output></p>
<button onclick="startWorker()">Start Worker</button> 开启worker线程
<button onclick="stopWorker()">Stop Worker</button> 停止worker线程
<br /><br />
<script>
var w;
function startWorker()
{
if(typeof(Worker)!=="undefined") //浏览器是否支持
{
if(typeof(w)=="undefined")
{
w=new Worker("demo_workers.js");//参数就是Worker线程要执行的代码文件
}
w.onmessage = function (event) {
document.getElementById("result").innerHTML=event.data;
};
## w是在主线程下的变量,onmessage监听Worker线程,
## 接收Worker线程在"demo_workers.js"文件通过
## postMessage()传回消息
}
else
{
document.getElementById("result").innerHTML="Sorry, your browser
does not support Web Workers...";
}
}
function stopWorker()
{
w.terminate(); //停止worker线程
}
</script>
</body>
</html>
"demo_workers.js"文件
var i=0;
function timedCount()
{
i=i+1;
postMessage(i); //它用于向JS主线程传回一段消息
setTimeout("timedCount()",500);
}
timedCount();
WebWorker与SharedWorker的区别
- WebWorker是属于一个Tab页面下的线程,不会和其他页面的Render进程(浏览器内核进程)共享
- SharedWorker是浏览器所有页面共享的,是浏览器单独创建的进程。不能采用与Worker同样的方式实现,因为它不隶属于某个Render进程,可以为多个Render进程共享使用
宏任务与微任务
顺序:宏任务 > 所有微任务 > GUI渲染线程 > 下一个宏任务