事件循环机制

在看到面试的时候的高频问题,今天我们就来深入的了解事件循环机制,因为之前总是看到,总是会忘记,今天就认真的总结出一篇专栏。

Js是单线程的解释性语言,在执行任务的过程中。但是在执行任务的时候会有同步任务异步任务之分。

  • 所有的同步任务会在主线程上先执行。
  • 并且异步的任务会被放在even table中被注册,注册之后会函数被移入到event queue
  • 当主线程完成完毕之后,会把event queue里读取对应的函数进入主线程进行执行。
  • 上述过程不断重复就是常说的事件循环机制。

如果又有异步的事件,会继续分发到对应的队列中,继续执行。其中就出现了宏任务微任务的区分。但是我自己的理解是,宏任务微任务的交替才构成了事件循环。

下文是从网上一篇文章中摘录的例子,看到评论区对于一些细节的争执,本着实践出真知的原则,我去拿来实践了一下。

原文链接:https://www.jianshu.com/p/12b9f73c5a4f/

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
console.log('golb1');			//同步任务,第一个被执行

setTimeout(function() { //setTimeout作为任务分发器,将任务分发到了宏任务队列
console.log('timeout1'); //第一轮的循环结束,第二轮的开始,将timeout1输出
process.nextTick(function(){//第二轮,将对于的nextTick输出到对应的微任务队列中
console.log('timeout1_nextTick');
})
new Promise(function(resolve) { //输出timeout1_promise,promise也进微任务
console.log('timeout1_promise');
resolve();
}).then(function() {
console.log('timeout1_then')
})
})

setImmediate(function() { //同样存在宏任务中
console.log('immediate1');
process.nextTick(function() {
console.log('immediate1_nextTick');
})
new Promise(function(resolve) {
console.log('immediate1_promise');
resolve();
}).then(function() {
console.log('immediate1_then')
})
})

process.nextTick(function() { //被存放在微任务队列
console.log('glob1_nextTick');
})
new Promise(function(resolve) { //new的promise的实例化过程中,还是属于同步的
console.log('glob1_promise'); //第二个输出了glob1_promise
resolve(); //改变状态,将then推入微任务队列
}).then(function() { //很快啊,马上在这个宏任务结束后,就立马执行了微任务
console.log('glob1_then')
})

setTimeout(function() { //被存放在了微任务队列
console.log('timeout2');
process.nextTick(function() {
console.log('timeout2_nextTick');
})
new Promise(function(resolve) { //这里是实例化如同上面的原理
console.log('timeout2_promise'); //这里输出了glob1_promise
resolve();
}).then(function() { //将then推入微任务队列
console.log('timeout2_then')
})
})

process.nextTick(function() { //存放在了微任务队列
console.log('glob2_nextTick');
})
new Promise(function(resolve) {
console.log('glob2_promise');
resolve();
}).then(function() {
console.log('glob2_then')
})

setImmediate(function() { //放在了宏任务队列中
console.log('immediate2');
process.nextTick(function() {
console.log('immediate2_nextTick');
})
new Promise(function(resolve) {
console.log('immediate2_promise');
resolve();
}).then(function() {
console.log('immediate2_then')
})
})

/*
(node.js v12.16.2的结果)
输出结果:
golb1
同步任务,先输出得到glob1没有问题
glob1_promise
promise在实例化的过程中,他会被执行。然后输出到glob1_promise
glob2_promise
这里的输出和上面的原理一致。
glob1_nextTick
到这里为止,此时的微任务队列中既有nextTick又有Promise,其中的nextTick队列是会被优先执行,这里是我从网上看到的,然后实践的时候,我有这样的思考,因为我的nextTick和promise是交替出现的,但是到微任务的处理时候,是两个nextTick先执行了,那么说,每个异步任务有他自己的一个任务队列存在的。这样才会存在同时输出了nextTick,同时也能印证nextTick队列的优先级别是比Promise.then队列高的。
glob2_nextTick

glob1_then

glob2_then
至此为止,当前的可执行的微任务执行完毕了,这一轮的循环也结束了。下一轮的循环从宏任务开始执行。
timeout1
第二轮循环的时候,将timeout1输出
timeout1_promise
此处的promise和上面一样
timeout1_nextTick
此时的结果,让我们印证了一个想法,那就是宏任务的队列是不会执行完,再去执行微任务的,他是在宏任务完成一个是时候,此时会先坚持微任务的队列,如果微任务的队列不空的话,立刻去执行微任务队列。
timeout1_then
顺序的nextTick会比Promise.then优先级高,不奇怪,剩下的也顺理成章了。同时在试验过程中。我还尝试着调换了位置,比如nextTick和promise的位置,以及timeout和immediate的位置,结果并没有发生改变,说明了这几种的优先顺序是timeout>immediate的。
timeout

timeout2_promise

timeout2_nextTick

timeout2_then

immediate1

immediate1_promise

immediate1_nextTick

immediate1_then

immediate2

immediate2_promise

immediate2_nextTick

immediate2_then
*/

下面给一个链接,链接里的图画的比较符合实际:

https://www.cnblogs.com/amiezhang/p/11349450.html