浏览器线程

按照谷歌浏览器来说,其一个 tab 页签是一个进程,每个进程中存在多个线程

进程:资源分配的最小单位

线程:程序执行的最小单位

常见的浏览器线程包括以下几种:

  • GUI 线程,当需要渲染 html 页面或者重绘页面时,该线程就会执行。

  • js 线程,用来执行 js,一直等待着任务队列中的任务到来,然后解析 js 脚本,执行代码。一个进程中永远只有一个 js 线程在运行 js 程序。当 js 线程活跃时,会中断 GUI 线程,该线程和 GUI 线程是互斥运行的。

  • 定时器线程,当 setTImeOut 和 setInterval 被调用时,会被专门的定时器线程处理。当计数完成后,其回调函数会被加入任务队列末尾,等待 js 引擎处理

  • http 网络服务线程,用来处理 http 请求等。当请求完成有回调函数时,会把该回调函数加入任务队列末尾,等待 js 引擎处理。

  • 事件处理线程,demo 中的每一个事件被触发,都会起一个被浏览器事件处理线程处理。当事件被触发时,会把事件函数加入任务队列末尾,等待 js 引擎处理

任务

一般分为同步任务和异步任务,js 主线程直接执行同步任务,异步任务会进入任务队列中,任务队列主要分为两种,即宏任务队列和微任务队列

宏任务

一般的 setTimeOut,setInterval,script 主文件,ajax 请求的回调函数,I/O 操作等都属于宏任务

微任务

一般 premise 的 then 回调,process.nextTikc()等属于微任务

事件循环

在 js 遇到 script,把它放在宏任务队列中。在解析的过程中,遇到同步代码,会顺序执行。遇到异步代码,则判断是宏任务还是微任务,宏任务放在宏任务队列的末尾,微任务放到微任务末尾。

当一个宏任务执行完成后,会立即执行微任务队列中的微任务。微任务中产生的宏任务或微任务,按之前的方式分别放置在对应队列中。只有当前微任务队列中的所有微任务全部执行完成后,才会执行下一个宏任务。

这个循环执行的过程,就是事件循环。当所有任务执行完毕后,js 线程会等待任务队列中任务的到来,一旦有新任务到来,则继续重复以上动作。

例子

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
36
37
38
39
40
new Promise((resolve) => {
setTimeout(() => {
console.log(666);
new Promise((resolve) => {
resolve();
}).then(() => {
console.log(777);
});
});
resolve();
})
.then(() => {
new Promise((resolve) => {
resolve();
})
.then(() => {
console.log(111);
})
.then(() => {
console.log(222);
});
})
.then(() => {
new Promise((resolve) => {
resolve();
})
.then(() => {
new Promise((resolve) => {
resolve();
}).then(() => {
console.log(444);
});
})
.then(() => {
console.log(555);
});
})
.then(() => {
console.log(333);
});

输出:
111,222,333,444,555,666,777