JavaScript事件循环

2022/4/2 JavaScript事件循环

# 浏览器的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

这个函数会被放到入调用栈中,执行会立即结束,并不会阻塞后续代码的执行;

image-20220317093934513

# 微任务和宏任务

事件循环中并非只维护着一个队列,事实上是有两个队列

  • 宏任务队列(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

# 面试题

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

image-20220317104315774

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

image-20220317110606452

Last Updated: 2022/12/12下午11:51:23