JavaScript事件循环机制的核心是执行栈、宏任务队列与微任务队列的协同:同步代码入栈执行,栈空后立即清空全部微任务,再执行一个宏任务。例如console.log('1'); Promise.then(() => console.log('2')); setTimeout(() => console.log('3'), 0); console.log('4'); 输出为1→4→2→3。

JavaScript 执行栈和任务队列不是各自独立运行的,而是由事件循环(Event Loop)统一调度、紧密配合的协作关系:执行栈负责同步代码的即时执行,任务队列暂存异步回调,事件循环则在栈空时按优先级把任务“搬”进栈里。
执行栈只处理同步任务
它是一个后进先出(LIFO)结构,所有同步代码(比如函数调用、console.log、new Promise() 构造函数本身)都会立刻入栈、顺序执行、逐个弹出。一旦栈清空,就代表当前这一轮同步逻辑彻底结束。
- 全局脚本最先入栈,执行完即出栈
- 遇到函数调用,新函数上下文压入栈顶;返回时立即弹出
- 无限递归会导致栈溢出(RangeError)
任务队列分宏任务和微任务两类
异步操作(如 setTimeout、fetch、点击事件)不会阻塞主线程,而是把回调函数交给对应的任务队列暂存——不是一股脑塞进同一个队列,而是严格区分:
- 微任务队列:Promise.then/catch/finally、queueMicrotask()、MutationObserver 回调
- 宏任务队列:setTimeout、setInterval、I/O、UI 渲染、script 标签加载
微任务优先级更高,只要执行栈一空,就会一次性清空整个微任务队列,再取下一个宏任务。
事件循环是它们之间的“调度员”
它持续运行,每一轮循环都遵循固定节奏:
- 先执行一个宏任务(比如从 setTimeout 来的回调)
- 宏任务执行完毕,检查并立即执行全部微任务(不管有几个,全跑完)
- 可选:浏览器可能在此刻进行一次 UI 渲染
- 再取下一个宏任务,重复上述流程
例如:console.log('1'); Promise.resolve().then(() => console.log('2')); setTimeout(() => console.log('3'), 0); console.log('4'); 输出顺序是 1 → 4 → 2 → 3,正体现了“同步→微任务→宏任务”的调度逻辑。
常见误区提醒
很多人以为“先到先得”,其实任务进入队列的时间不等于执行时间。关键在于:微任务总在当前宏任务结束后、下一个宏任务开始前执行;而多个 setTimeout 即使延迟都是 0,也仍是宏任务,会排在所有微任务之后。
理解这个协同机制,才能真正看懂异步代码的执行时序,避免因预期错位导致的 bug。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











