什么是 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 协议 ,转载请注明出处!