可视化JavaScript事件循环原理

前言

众所周知,JavaScript是单线程的,它一次只能处理一个任务。如果所有的代码运行时都需要等待前一个代码块运行完成才能执行,那对于i/o密集型任务来说时间带来的消耗是无法想象的,由此引出了JavaScript事件循环概念,通过JavaScript事件循环,我们能够智能地对任务调度做出合理规划,不会让整个程序系统看起来像”崩了“一般静止不动。

英文术语解析:

JavaScript RunTime:JavaScript运行时
JavaScript Engine:JavaScript引擎
heap:内存堆
call stack:调用栈
web APIs:网络接口
event loop:事件循环
task queue:宏任务队列
microtask queue:微任务队列

一 普通任务的调用方式

二 web APIs的具体形式

三 web APIs与引擎、设备交互

四 具体的宏任务有哪些

常见的宏任务有setTimeout、setInterval、 xhr.onload、I/O、UI渲染等。

五 web APIs与宏任务队列的事件循环过程

六 具体的微任务有哪些

微任务仅由promise的回掉.then、await之后的异步函数体(不包括await 同行)、queueMicrotask()、MutationObserver、process.nextTick()(Node.js 特有)等。

七 微任务队列的事件循环过程

八 微任务与宏任务的关系

事件循环优先处理微任务队列内的任务而不是宏任务队列的任务,当宏任务队列进行时,会检查微任务队列内的任务是否存在,有就会优先执行微任务。

九 特殊情况-无限微任务循环

十 小结

请分析下面代码块的输出结果,验证你的学习成果,蒙住答案,好好思考一下。

题目1:

1
2
3
4
5
6
7
8
9
10
11
Promise.resolve()
.then(()=>console.log(1));

setTimeout(()=>console.log(2),10);

queueMicrotask(()=>{
console.log(3);
queueMicrotask(()=>console.log(4))
});
console.log(5);

答案:51342

解析:
代码自上而下开始执行:
首先,Promise.resolve().then 率先注册了一个微任务 1;接着 setTimeout 开启 10ms 定时器准备推入宏任务;随后 queueMicrotask 将回调函数 3 推入微任务队列;最后执行同步代码,直接打印 5
同步代码执行完毕后,事件循环开始清空微任务队列:
率先取出最先排队的微任务,打印 1;紧接着取出第二个微任务,打印 3。在此过程中,内部的 queueMicrotask 又将 4 动态推入了当前的微任务队列。由于事件循环会一次性连续清空所有微任务(包含微任务衍生出的新微任务),因此不会停顿,直接继续取出新微任务并打印 4
当微任务队列彻底清空后,事件循环进入宏任务阶段,执行到期的定时器回调,最终打印 2

题目2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
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')

答案:

1
2
3
4
5
6
7
8
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

解析:整体 script 作为第一个宏任务进入主线程,代码自上而下开始执行同步任务:
首先,直接输出 script start;遇到 setTimeout 时,将其回调推入宏任务队列;接着调用 async1(),输出 async1 start。在遇到 await async2() 时,由于 async/await 本质是 Promise + Generator 的语法糖,它会先同步执行 async2() 并输出 async2,随后让出线程,将 await 后续的代码包装成微任务放入微任务队列,并跳出 async1 函数。
继续向下执行,遇到 new Promise 立即执行其构造函数,输出 promise1,并将 .then() 中的回调推入微任务队列;随后执行完最后一行同步代码,输出 script end
此时主线程空闲,事件循环开始依次清空微任务队列,先后输出 async1 endpromise2。本轮微任务清空完毕后,再进入下一轮宏任务,读取并执行 setTimeout 的回调,最终输出 setTimeout

如有疑问欢迎在评论区留言!期待你的评论!

十一 参考文献

JavaScript Visualized - Event Loop, Web APIs, (Micro)task Queue
深入理解 JavaScript 之事件循环(Event Loop)
事件循环:微任务和宏任务

十二 GIF制作工具

ezgif

原创文章,转载请标明出处:
Made by Eli Bi

感谢您的支持 | Thank you for supporting

可视化JavaScript事件循环原理
http://example.com/2026/07/21/JavaScript_Event Loop-Web_APIs-(Micro)task Queue/
作者
Eli Bi
发布于
2026年7月21日
更新于
2026年8月5日
许可协议