# 浏览器事件循环机制

事件循环机制

# 概念

# Task(宏任务)

同步代码、setTimeout 回调、setInteval 回调、IO、UI 交互事件、postMessage、MessageChannel。

# 微任务

MicroTask(微任务):Promise 状态改变以后的回调函数(then 函数执行,如果此时状态没变,回调只会被缓存,只有当状态改变,缓存的回调函数才会被丢到任务队列)、Mutation observer 回调函数、queueMicrotask 回调函数(新增的 API)。

# Event Loop 执行顺序

  • 执行同步代码
  • 执行完所有同步代码后且执行栈为空,判断是否有微任务需要执行
  • 执行所有微任务且微任务队列为空
  • 是否有必要渲染页面
  • 执行一个宏任务

# 案例解释

console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
    queueMicrotask(() => console.log('queueMicrotask'))
    console.log('promise');
});

console.log('script end');
1
2
3
4
5
6
7
8
9
10
11
12
  1. 遇到 console.log 执行并打印

  2. 遇到 setTimeout,将回调加入宏任务队列

  3. 遇到 Promise.resolve(),此时状态已经改变,因此将 then 回调加入微任务队列

  4. 遇到 console.log 执行并打印。此时同步任务全部执行完毕,分别打印了 'script start' 以及 'script end',开始判断是否有微任务需要执行。

  5. 微任务队列存在任务,开始执行 then 回调函数

  6. 遇到 queueMicrotask,将回到加入微任务队列

  7. 遇到 console.log 执行并打印

  8. 检查发现微任务队列存在任务,执行 queueMicrotask 回调

  9. 遇到 console.log 执行并打印。此时发现微任务队列已经清空,判断是否需要进行 UI 渲染。

  10. 执行宏任务,开始执行 setTimeout 回调

  11. 遇到 console.log 执行并打印,执行一个宏任务即结束,寻找是否存在微任务,开始循环判断...

async function async1() { 
    console.log('async1 start')
    await async2() 
    await async3() 
    await async4() 


    console.log('async1 end')
}
async function async2() {
    console.log('async2')

    await async5() 
    console.log('async2 async2')


}
async function async3() {
    console.log('async3')
}
async function async4() {
    console.log('async4')
}
async function async5() {
    console.log('async5')
}


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')


// script start
// async1 start
// async2
// async5
// promise1
// script end
// async2 async2
// promise2
// async3
// async4
// async1 end
// setTimeout




async function sleep(ms,arg) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(arg)
        }, ms);
    })
}
async function async1() { 
    console.log('async1 start')
    await async2() 
    await async3() 
    await async4() 
    console.log('async1 end')
}
async function async2() {
    console.log('async2')
    await async5() 
    const x = await sleep(5000, 'async2 -sleep')
    console.log(x)
    console.log('async2 async2')
}
async function async3() {

    console.log('async3')
    const y = await sleep(8000, 'async3 -sleep')
    console.log(y)


}
async function async4() {
    console.log('async4')
}
async function async5() {
    console.log('async5')
}



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')

// script start
// async1 start
// async2
// async5
// promise1
// script end
// promise2
// setTimeout
// async2 -sleep
// async2 async2
// async3
// async3 -sleep
// async4
// async1 end


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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

# 参考

  1. 任务,微任务,队列 (opens new window)
  2. HTML Standard (opens new window)
  3. MutationObserver 只是浮云,microtask 才是核心 (opens new window)
  4. 从 event loop 规范探究 javaScript 异步及浏览器更新渲染时机 (opens new window)
  5. 从 Vue.js 源码看 nextTick 机制 (opens new window)
  6. nextTick 为什么要 microtask 优先? (opens new window)
  7. 浏览器的工作原理:新式网络浏览器幕后揭秘 (opens new window)
  8. JavaScript 事件循环 (opens new window)