一、进程和线程
程序在运行的时候需要内存,进程就是程序可以使用的内存空间和资源,每个程序至少有一个进程,进程之间相互独立。 线程是执行程序的具体路径,可以使用进程的内存空间和资源运行代码。一个进程至少有一个线程,这个线程就是程序的主线程。
二、浏览器的进程和线程
浏览器是多进程多线程的应用。由于浏览器有很多复杂的功能,使用多个线程进行隔离,一个线程崩溃不会影响其他线程。浏览器的主要进程有:
- 浏览器进程
- 网络进程
- 渲染进程
浏览器进程负责浏览器的功能,如导航栏、地址栏、书签等,网络进程负责页面的网络功能,渲染进程负责页面的渲染,如解析HTML、CSS等、执行js等。
前端的大多数工作都在渲染进程中完成。浏览器的每一个Tab页都是一个进程,当打开较多的tab页时,会占用比较多的内存。
三、事件循环
事件循环也叫消息循环,是主线程不断的从事件队列中读取事件并执行的过程,任务没有优先级在队列中先进先出。
任务的优先级由它所在的任务队列决定,同一个类型的任务只能在一个任务队列中排队,不同类型的任务可以出现在不同的任务队列中。所有的浏览器中都必须有微任务队列。还有在之前被称位宏任务和微任务队列,而随着浏览器的更加复杂化,之前的宏任务和微任务队列已经不再能满足,取而代之的事更精细化的任务队列。例如在chorme中被分为、微任务队列、用户交互任务队列、定时器任务队列。微任务队列的优先级最高,只有当它里面的任务执行完后才会执行其它任务队列的任务。其次是用户交互任务队列、最后才是定时器任务队列。
- 全局js代码是一个任务
- 微任务包括:Promise、MutationObserver
四、Js为什么会阻塞页面渲染?
function delay( time){
const startTime = Date.now();
while(Date.now() - startTime < time){}
}
document.write("start");
delay(5000);
执行过程:全局js代码作为任务进入主线程执行,document.write设置了页面内容,执行delay主线程阻塞5s, 5s后主线程执行渲染。
原因: document.write只是设置了文档内容,实际的更新要等浏览器再次渲染, 而这时主线程的全局JS任务并没有执行完,而是被while循环阻塞了5s, 5s后主线程不再被占用,这时才可以执行渲染。
五、浏览器渲染进程为什么是单线程
浏览器要做的事情其中包含解析css、解析html、执行js, 而js的功能之一就是操作dom, 改变html结构、内容和样式, 如果是多线程就会具有极不稳定的竞态性, 即当两个线程同时修改dom的时候以哪一个为准呢?而单线程能避免这些复杂的竞态问题, 从而保持渲染的稳定性。
- 单线程避免了复杂的竞态问题, 从而保持了渲染的稳定性。
- 单线程避免复杂的多线程问题,降低开发难度,例如在多线程中要处理数据同步、死锁等问题。
六、常见题目
6.1 讲一下浏览器的事件循环
由于浏览器的渲染进程的渲染主线程只有一个, 它通过异步的方式避免阻塞, 而异步的具体实现方式就是通过事件循环。事件循环是指浏览器不断的从事件队列中读取任务并执行的过程, 主线程从全局JS任务开始执行, 遇到异步代码交给其它线程去处理, 比如网络线程、定时器线程等,再这些线程处理完之后会把回调事件包装成任务放入事件队列中, 主线从事件队列中读取任务并执行。任务本身没有优先级,他符合队列的特性总是先进先出,任务的优先级由它所在的任务队列决定。每个浏览器都必须有微任务队列, 还微任务和宏任务队列,但是随着浏览器的更加复杂,宏任务和微任务队列已经不再能满足,取而代之的是更加精细化的任务队列, 例如在chorme浏览器中有: 微任务队列、交互任务队列、计时器任务队列。同一类任务只能出现在同一个任务队列中, 不同类的任务可以出现在不同的任务队列中, 任务队列的优先级顺序是微任务队列、交互任务队列、计时器任务队列。
6.2 下面的输出结果是?
setTimeout(() => {
console.log(1);
Promise.resolve().then(() => {
console.log(2);
});
}, 0);
setTimeout(() => {
console.log(3);
}, 0);
Promise.resolve().then(() => {
console.log(4);
});
console.log(5);