一 、进程与线程
1.浏览器是多进程的
它主要包括以下进程:
- Browser进程:浏览器的主进程,唯一,负责创建和销毁其它进程、网络资源的下载与管理、浏览器界面的展示、前后进退等。
- GPU进程:用于3D绘制等,最多一个。
- 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建。
浏览器渲染进程(浏览器内核):内部是多进程的,每打开一个网页就会创建一个进程。主要用于页面渲染,脚本执行、事件处理等。
2.渲染进程(浏览器内核)
浏览器的渲染进程是多线程的,页面的渲染,javascript的执行、事件的循环都在这个进程内执行:
GUI渲染线程:负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。
- javascript引擎线程:也称为javascript内核,负责处理javascript脚本程序、解析javascript脚本、运行代码等(例如v8引擎)
- 事件触发线程:用来控制浏览器事件循环,注意这不归javascript引擎线程管,当事件被触发时,该线程会把事件的回调函数添加到待处理队列的队尾,等待javascript引擎的处理。
- 定时触发器线程:传说中的setTimeout和setInterval所在线程,注意W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
- 异步http请求线程:在XMLHttpRequest连接后通过浏览器新开一个线程请求,将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由javascript引擎执行。
注意,GUI渲染线程与javascript引擎线程是互斥的,当javascript引擎执行时
GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到javascript引擎空闲时立即被执行。所以如果javascript执行时间过长,这样就会造成页面的渲染不连贯,导致页面加载阻塞。
二、单线程的javascript
所谓单线程,是指在javascript引擎中负责解释和执行javascritp代码的线程唯一,同一时间上只能执行一件任务。
问题:为什么要引入单线程
我们知道;
- 浏览器需要渲染Dom
- javascript可以更改Dom
- javascritp执行时,浏览器Dom渲染停止
如果javascript引擎线程不是单线程的,那么可以同时执行多端javascript,如果这多段javascript都修改Dom,那么就会出现Dom冲突。
你可能会说 web worker 支持多线程,但是web worker不能访问window对象、document对象等。
原因:避免DOM渲染的冲突
当然,我们可以为浏览器引入锁的机制来解决这些冲突,但其大大提高了复杂性,所以javascript从诞生开始就选择了单线程执行。
引入单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务,这同时又导致一个问题,如果前一个任务耗时很长,后一个任务就不得不一直等着。
// 实例1
let i, sum = 0
for(i = 0; i < 1000000000; i ++) {
sum += i
}
console.log(sum)
在实例1中,sum并不能立刻打印出来,必须在for循环执行完之后才能执行console.log(sum)
// 实例2
console.log(1)
alert('hello')
console.log(2)
在实例2中,浏览器先打印1,然后弹出弹框点击确定后才执行console.log(2)
总结:
- 优点:实现比较简单,执行环境相对单纯。
- 缺点:只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其它任务无法执行。
为了 解决这个问题,javascript语言将任务的执行模式分为两种:同步和异步。