浏览器架构和事件循环

chrome浏览器架构

Chrome 采用多进程架构,其顶层存在一个 Browser process 用以协调浏览器的其它进程。

  • Browser Process:
  1. 负责包括地址栏,书签栏,前进后退按钮等部分的工作;

  2. 负责处理浏览器的一些不可见的底层操作,比如网络请求和文件访问;

  • Renderer Process:
  1. 负责一个 tab 内关于网页呈现的所有事情
  • Plugin Process:
  1. 负责控制一个网页用到的所有插件,如 flash
  • GPU Process
  1. 负责处理 GPU 相关的任务

image-20210930012419754

由于一个tab标签页都有一个独立的渲染进程,所以一个tab异常崩溃后,其他tab不会受到影响。

一个渲染进程包括

  • JS引擎线程
  • HTTP请求线程
  • 定时触发线程
  • 事件触发线程
  • GUI线程

浏览器JS异步执行原理

执行JS代码的线程只有一个,是浏览器提供的JS引擎线程,浏览器中还有

image-20210930012033902

事件循环

image-20210930190158487

js引擎解析代码时,遇到同步任务->放入执行栈直接执行,遇到异步任务(比如ajax),交给网络线程处理,完成后将对应回调放入异步任务队列,当执行栈清空时,循环检测异步任务队列,将异步队列中的回调放入执行栈中执行。

宏任务和微任务

Dom渲染会在微任务结束后,宏任务执行前

宏任务

# 浏览器 Node
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame

微任务

# 浏览器 Node
process.nextTick
MutationObserver
Promise.then catch finally

事件循环加入宏任务微任务

  1. Call Stack调用栈清空
  2. 清空当前微任务队列
  3. 尝试DOM渲染
  4. 执行event loop
  5. 取出任务队列里的一个宏任务,入栈执行
  6. 返回1

对于以下demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
new Promise((r) => {
r();
})
.then(() => console.log(1))
.then(() => console.log(2))
.then(() => console.log(3))

new Promise((r) => {
r();
})
.then(() => console.log(4))
.then(() => console.log(5))
.then(() => console.log(6))

// 输出
// 1 4 2 5 3 6

Promise.prototype.then() 会隐式返回一个新 Promise

如果 Promise 的状态是 pending,那么 then 会在该 Promise 上注册一个回调,当其状态发生变化时,对应的回调将作为一个微任务被推入微任务队列

如果 Promise 的状态已经是 fulfilled 或 rejected,那么 then() 会立即创建一个微任务,将传入的对应的回调推入微任务队列

对于以下demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
console.log('start')
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise in timeout');
resolve();
});

console.log('promise after timeout');
}).then(() => {
console.log('promise4');
Promise.resolve().then(() => {
console.log('7777')
})
}).then(() => {
console.log('promise5');
});

setTimeout(() => {
console.log('99999');
}, 0);
Promise.resolve().then(() => {
console.log('promise3');
});

console.log('end');
// 输出:
// start
// promise after timeout
// end
// promise3
// promise in timeout
// promise4
// 7777
// promise5
// 99999