浏览器进程与线程
进程:cpu分配资源的最小单位;线程:cpu调度的最小单位。进程之间是独立的,同一个进程中可以有多个线程,多个线程共享同一进程中的资源。
浏览器主要的进程类型有:
-
浏览器进程(Browser Process):
浏览器的主进程(负责协调、主控),只有一个,它控制浏览器的主窗口和各个子进程的创建和销毁,并协调他们之间的交互和通信。同时,它负责管理用户界面、存储缓存和历史记录等功能。浏览器窗口和标签页的创建和关闭也由此进程执行。 -
渲染进程(Renderer Process)
该进程主要负责网页的呈现和交互功能,每个标签页都有一个独立的渲染进程来负责其呈现。这种方式可以增强浏览器的稳定性,防止一个网页的崩溃引起整个浏览器的崩溃。当用户打开一个新的标签页或者输入一个URL时,浏览器会分配一个新的渲染进程来处理该页面的显示。
-
插件进程(Plug-in Process)
该进程主要负责运行浏览器中的插件, 在使用插件,如Adobe Flash、Java等,浏览器需要独立的插件进程来运行插件。这保证了插件崩溃或者运行时出现问题时不会影响到主要的浏览器进程或其他标签页。 -
GPU进程(GPU Process)
该进程主要负责处理网页中的图像和视频,它是使用浏览器的硬件加速技术实现的。GPU进程主要负责处理和管理GPU相关的操作和资源,同时确保GPU的安全和稳定性, 负责3D作图和使用GPU加速的网页效果的运行。现代浏览器因为许多3D、CSS3等等视觉效果的运用,需要使用强大的图像和GPU加速技术,因此需要单独的进程来处理这些任务。
每个标签页都分别是一个进程,资源不共享。渲染进程中不同线程分别有不同的功能:
GUI渲染线程 => 解析html、css,构建dom树 => 布局 => 绘制
JS引擎线程 => 解析执行js脚本,即执行执行栈中的代码,负责取事件队列中的时间
定时器触发线程 => 接收JS引擎分配过来的定时器任务并计时 => 执行完成之后交给事件触发线程
网络请求线程 => 接收JS引擎分配过来的异步请求任务 => 执行完成后交给事件触发线程
交互线程 => 接收用户的动作事件、点击、scroll、窗口大小改变、键盘等等=>并处理事件交给事件触发线程
事件触发线程 => 将接收到的事件放入事件队列的队尾(会放到对应不同类型的队列中)
可知每个进程中只有一个JS引擎,即JS是单线程的。
GUI渲染线程和JS引擎线程互斥,当GUI执行的时候JS挂起,当JS执行的时候GUI挂起。即该进程在执行脚本的时候就不能渲染。当执行某些任务的时候(网络请求、定时器、用户事件),js引擎被阻塞去等待该事件的完成肯定是最差的做法,解决方式是将异步任务与事件循环。以上的线程共享一个消息队列,进行着事件循环:
事件循环
js引擎永远在做一个循环:执行执行栈中的代码、(将异步任务分担出去)、取出消息队列中的事件放入执行栈。
以前会有宏队列微队列的说法,微队列中的事件优先级高于宏队列,先将微队列执行空再执行宏队列。但现在又把宏队列细分为各种不同类型的队列,用来放置不同类型的事件(用户交互事件、网络请求事件、定时器事件…),分配了不同的优先级(如用户事件比定时器事件优先级高),anyway,微队列总是最高的。
添加任务到微队列的主要方式主要是使用 Promise、MutationObserver
例如:
// 立即把一个函数添加到微队列
Promise.resolve().then(函数)
面试题(可以多去找点该类型的题)
setTimeout(function (){
// 宏任务
console.log(1);
setTimeout(function() {
// 宏任务 =》 宏任务
console.log(2);
}, 0);
new Promise(function(resolve) {
// 宏任务 =》 同步
resolve();
console.log(3);
}).then(function() {
// 宏任务 =》 微任务
console.log(4);
});
}, 0)
// 同步
console.log(5);
setTimeout(function() {
// 宏任务
console.log(6)
})
// 执行结果:5 1 3 4 6 2