事件循环是主线程在调用栈清空后,主动从微任务队列(优先清空)和宏任务队列(每次取一个)中持续取任务执行的动态闭环机制。

事件循环不是“等任务来了再处理”,而是持续检查、搬运、执行的动态过程。它的本质,是主线程在调用栈清空后,主动从队列中取任务推入栈中运行,形成一个永不停歇的闭环。
调用栈:同步代码的执行现场
所有函数调用都压入调用栈,按后进先出(LIFO)顺序执行。函数执行完立即出栈,直到栈完全清空——这标志着本轮同步代码结束。
- 遇到 console.log、变量赋值、普通函数调用 等,直接入栈执行
- 遇到 setTimeout、Promise构造器 等异步操作,不入栈,而是交由浏览器API托管
- 栈一旦卡住(如无限递归),整个JS线程就阻塞,页面无响应
Web API:异步任务的“托管中心”
当代码触发异步操作(如定时器、fetch、DOM事件监听),JS引擎不等待结果,而是把回调函数交给对应的Web API模块管理。
- setTimeout/setInterval → 由定时器模块计时,到期后将回调放入宏任务队列
- Promise.then/catch/queueMicrotask → 由Promise模块处理,解析完成后将回调放入微任务队列
- click、input等DOM事件 → 由事件系统捕获,回调进入宏任务队列(部分情况可能走微任务)
任务队列:分优先级的等待区
不是只有一个队列,而是两个独立队列并存:
- 微任务队列(Microtask Queue):存放 Promise.then、queueMicrotask、MutationObserver 等。每次宏任务结束后,它会被一次性全部清空
- 宏任务队列(Macrotask Queue):存放 setTimeout、setInterval、script整体、I/O、UI渲染后触发的事件等。每次只取最先进入的一个执行
注意:微任务永远插在宏任务之间,且不跨轮次——上一个宏任务刚结束,立刻执行所有当前微任务,不会等下一个宏任务进来再处理。
Event Loop:串联三者的调度员
它不做计算,只做三件事:
- 确认调用栈是否为空
- 若为空,先检查微任务队列;不为空则逐个执行,直到清空
- 再取一个宏任务执行,然后重复上述流程
典型执行节奏:同步代码 → 微任务(全执行)→ UI渲染(浏览器环境)→ 下一个宏任务。这个节奏决定了为什么 Promise.then 总比 setTimeout 先输出,哪怕两者都写成 0 毫秒延迟。


















