一、前言
部分参考:https://juejin.cn/post/6844903553795014663#heading-8
个人笔记知识点,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正。最后有笔试题
二、大纲
- 进程和线程
- 浏览器是多进程的
- 浏览器内核-渲染进程(五个渲染线程)
- 浏览器渲染步骤流程
- 回流与重绘
- 从Event Loop谈JS的运行机制
- 微任务,宏任务
- 微任务,宏任务笔试题
进程和线程
进程是由一个线程或者多个线程组成的。
他们关系相当于“工厂”与“打工人”关系
- 进程是一个工厂,工厂有它的独立资源
- 工厂之间相互独立
- 线程是工厂中的打工人,多个打工人协作完成任务
- 工厂内有一个或多个打工人
- 打工人(线程)之间共享空间
进阶
浏览器是多进程的
浏览器是多进程的。
例如:windows电脑中,可以打开任务管理器
浏览器包含了以下四个进程
-
Browser进程:浏览器的主进程
- 网络资源的管理,下载等
- 负责浏览器界面显示,与用户交互。如前进,后退等
-
第三方插件进程
-
GPU进程:用于3D绘制等
-
浏览器渲染进程(浏览器内核)
- 页面渲染,脚本执行,事件处理等
重点是浏览器内核(渲染进程)
浏览器的渲染进程是多线程的
1. GUI渲染线程
- 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
- 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
2. JS引擎线程
- 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
- JS引擎线程负责解析Javascript脚本,运行代码
js为单线程
** js是单线程的,也就代表js只能一件事情一件事情执行,那如果一件事情执行时间太久,
** 后面要执行的就需要等待,需要等前面的事情执行完成,后面的才会执行。****
同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,
这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
当JS引擎执行时GUI线程会被挂起(相当于被冻结了),
GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
3. 事件触发线程
-
归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
-
当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
-
当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
-
注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
4. 定时触发器线程
-
传说中的
setInterval
与setTimeout
所在线程 -
浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
-
因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
5. 异步http请求线程
-
在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
-
将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。
浏览器渲染步骤流程
浏览器器内核拿到内容后,渲染大概可以划分成以下几个步骤:
- 解析html建立dom树
- 解析css建立CSSOM树,然后结合DOM合并成render树
- 布局render树(Layout/reflow),负责各元素尺寸、位置的计算
- 绘制render树(paint),绘制页面像素信息
- 浏览器会将各层的信息发送给GPU,GPU会将各层合成,显示在屏幕上
渲染完毕后就是load
事件了,之后就是自己的JS逻辑处理了
回流与重绘
什么是回流
当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。
什么是重绘
当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
区别:
他们的区别很大:
-
每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建render tree。
-
回流必将引起重绘,而重绘不一定会引起回流
。比如:只有颜色改变的时候就只会发生重绘而不会引起回流
从Event Loop谈JS的运行机制
-
JS分为同步任务和异步任务
-
同步任务都在主线程上执行,形成一个
执行栈
-
主线程之外,事件触发线程管理着一个
任务队列
,只要异步任务有了运行结果,就在任务队列
之中放置一个事件。 -
一旦
执行栈
中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列
,将可运行的异步任务添加到可执行栈中,开始执行,如此循环这个步骤,被称为事件循环(Event Loop)
微任务,宏任务
概念:
宿主环境提供的叫宏任务,由语言标准提供的叫微任务
-
宿主环境宏任务:
简单来说就是能使javascript完美运行的环境,只要能完美运行javascript的载体就是javascript的宿主环境。目前我们常见的两种宿主环境有浏览器和node。
常见的两个宏任务:
-
语言标准微任务:
由语言标准提供的就是微任务,比如ES6提供的promise。
常见的微任务:- Promise.then()
- Object.observe
- MutationObserver
- process.nextTick(Node环境独有)
微任务和宏任务的工作流程(总结:有微则微,无微则宏)
- 看微任务队列是否有微任务
- 没有微任务执行下一个宏任务
- 有微任务将所有微任务执行
- 执行完微任务,执行下一个宏任务
微任务,宏任务面试题
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
async1();
new Promise(function (resolve) {
console.log('promise1')
resolve();
}).then(function () {
console.log('promise2')
})
console.log('script end')
这段代码的执行结果会是什么呢?
知识点:
解析:
1.await 执行完后,。async 标记的函数会返回一个 Promise 对象,等promise对面都执行完后再执行下面的语句**
2.await asyn2()后面的代码console.log(")相当于.then的微任务
最终答案为
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout