什么是 Event Loop?
JavaScript 是一门单线程、非阻塞的语言,它只有一个调用栈,每次只能做一件事情。 它的异步和多线程就是靠 Event Loop 事件循环机制来实现的。
JavaScript 代码是从上往下一行一行执行的。先将同步代码执行完,再执行异步代码。
Event Loop
组成部分:
- 调用栈(call stack)
- 任务队列(Task Queue)
- 微任务队列(Microtask Queue)
如下这段代码依次输出 2 -> 1 -> 3
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
执行顺序:
- f2() 压入调用栈,执行里面的代码
- console.log(2) 压入调用栈并执行,打印 2,然后弹出
- f1() 压入调用栈,执行里面的代码
- console.log(1) 压入调用栈并执行,打印 1,然后弹出
- f1() 执行完毕弹出
- console.log(3) 压入调用栈并执行,打印 3,然后弹出
- f2() 执行完毕弹出
任务队列
JavaScript 中的异步操作,例如 fetch、事件回调、setTimeout、setInterval 的回调函数会入队到任务队列。
看如下示例
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
执行顺序:
- f2() 压入调用栈,执行里面的代码
- setTimeout 压入调用栈时,它的回调会进入到任务队列,
- f1() 压入调用栈,执行里面的代码
- console.log(1) 压入调用栈并执行,打印 1,然后弹出
- f1() 执行完毕弹出
- console.log(3) 压入调用栈并执行,打印 3,然后弹出
- f2() 执行完毕弹出
- 当调用栈为空时,任务队列的消息被压入到调用栈中,并执行,打印 2 , 然后弹出
微任务队列
Promise、async、await 会加入到微任务队列中,会在调用栈清空时立即执行,并且新加入的微任务也会一起执行,
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
执行顺序:
- new Promise 构造函数,被压入调用栈
- console.log(4) 压入调用栈并执行,打印 4,然后弹出
- resolve(5) 压入调用栈并执行,然后弹出
- f2() 压入调用栈,执行里面的代码
- setTimeout 压入调用栈时,它的回调会进入到消息队列
- f1() 压入调用栈,执行里面的代码
- console.log(1) 压入调用栈并执行,打印 1,然后弹出
- f1() 执行完毕弹出
- console.log(3) 压入调用栈并执行,打印 3,然后弹出
- p.then(resolved => {console.log(resolved)}) 压入调用栈并执行,回调函数入队到微任务队列,然后弹出
- .then(() => { console.log(6)}) 压入调用栈并执行,回调函数入队到微任务队列 ,然后弹出
- f2() 执行完毕弹出
- console.log(resolved) 压入调用栈并执行,打印 5,然后弹出
- console.log(6) 压入调用栈并执行,打印 6,然后弹出
- 当调用栈为空时,消息队列的消息被压入到调用栈中,并执行,打印 2 , 然后弹出
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!