# 浏览器的JavaScript线程
JavaScript是单线程的,但是JavaScript的线程应该有自己的容器进程:浏览器或者Node。
目前多数的浏览器其实都是多进程的,打开一个tab页面时就会开启一个新的进程,这是为了防止一个页面卡死而造成所有页面无法响应,整个浏览器需要强制退出;
每个进程中又有很多的线程,其中包括执行JavaScript代码的线程;
JavaScript的代码执行是在一个单独的线程中执行的:
这就意味着JavaScript的代码,在同一个时刻只能做一件事;如果这件事是非常耗时的,就意味着当前的线程就会被阻塞;
所以真正耗时的操作,实际上并不是由JavaScript线程在执行的:
浏览器的每个进程是多线程的,那么其他线程可以来完成这个耗时的操作;比如网络请求、定时器,只需要在特性的时候执行应该有的回调即可;
# 浏览器的事件循环
如果在执行JavaScript代码的过程中,有异步操作:
function sum(num1,num2){
return num1 + num2
}
function bar(){
return sum(20,30)
}
setTimeout(()=>{
console.log(11111)
},1000)
const result = bar()
console.log(result)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这个函数会被放到入调用栈中,执行会立即结束,并不会阻塞后续代码的执行;
# 微任务和宏任务
事件循环中并非只维护着一个队列,事实上是有两个队列:
宏任务队列(macrotask queue):ajax、setTimeout、setInterval、DOM监听、UI Rendering等
微任务队列(microtask queue):Promise的then回调、Mutation Observer API、queueMicrotask()等
事件循环对于两个队列的优先级:
- main script中的代码优先执行(编写的顶层script代码);
- 在执行任何一个宏任务之前(不是队列,是一个宏任务),都会先查看微任务队列中是否有任务需要执行
- 也就是宏任务执行之前,必须保证微任务队列是空的;
- 如果不为空,那么就优先执行微任务队列中的任务(回调);
setTimeout(()=>{
console.log("setTimeout")
})
queueMicrotask(()=>{
console.log("queueMicrotask")
})
Promise.resolve({}).then(()=>{
console.log("Promise")
})
function foo(){
console.log("foo")
}
function bar(){
console.log("bar")
foo()
}
console.log("other code")
bar()
// other code
// bar
// foo
// queueMicrotask
// Promise
// setTimeout
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
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
# 面试题
setTimeout(() => {
console.log("setTimeout1")
new Promise((resolve, reject) => {
resolve()
}).then(() => {
new Promise((resolve, reject) => {
resolve()
}).then(() => {
console.log("then4")
})
console.log("then2")
})
})
new Promise((resolve, reject) => {
console.log("Promise1")
resolve()
}).then(() => {
console.log("then1")
})
setTimeout(() => {
console.log("setTimeout2")
})
console.log(2)
queueMicrotask(() => {
console.log("queueMicrotask1")
})
new Promise((resolve, reject) => {
resolve()
}).then(() => {
console.log("then3")
})
//Promise1
//2
//then1
//queueMicrotask1
//then3
//setTimeout1
//then2
//then4
//setTimeout2
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
41
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
41
async function async1() {
console.log("async1 start")
await async2()
new Promise((resolve, reject) => {
resolve()
}).then(() => {
console.log("then4")
})
console.log("async1 end")
}
async function async2() {
console.log("async2")
}
console.log("script start")
setTimeout(() => {
console.log("setTimeout")
}, 0)
async1()
new Promise((resolve, reject) => {
console.log("promise1")
resolve()
}).then(() => {
console.log("promise2")
})
console.log("script end")
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
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
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