JavaScript事件循环是单线程通过调用栈、宏任务队列和微任务队列协作实现异步非阻塞的核心机制;同步代码入栈执行,异步回调由Web API处理后按宏/微任务优先级进入对应队列,每轮先清空微任务再取一个宏任务。
JavaScript 的事件循环(Event Loop)是它实现异步非阻塞行为的核心机制,不是靠多线程,而是靠单线程 + 任务队列 + 调用栈协作完成的。
JS 是单线程语言,所有同步代码都在一个调用栈(Call Stack)中按顺序执行。遇到函数调用就压栈,返回就出栈。一旦栈空了,引擎不会停,而是去检查有没有待处理的“任务”。
console.log()、普通函数调用)直接进栈执行setTimeout、fetch、用户点击)不会卡住主线程,而是交由 Web API(浏览器)或 Node.js 环境处理任务队列其实分两类:宏任务(macrotask)和微任务(microtask),它们的执行优先级不同。
setTimeout、setInterval、I/O、UI 渲染、script 标签整体等Promise.then/catch/finally、MutationObserver、queueMicrotask()
这就是为什么 Promise 总比 setTimeout 先输出——它属于更高优先级的微任务队列。
用伪代码描述一次循环:
setTimeout 回调)
romise → 立即执行 executor,then 注册进微任务队列;遇到 setTimeout → 交由浏览器延时,到期后推入宏任务队列看这段代码:
console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); console.log(4);
输出顺序是:1 → 4 → 3 → 2。因为:
1 和 4 是同步,立刻执行Promise.then 是微任务,排在当前宏任务结束后、下个宏任务前setTimeout 是宏任务,要等本轮全部微任务走完,再等下一轮循环才执行基本上就这些。理解事件循环不靠死记,关键是分清“谁进栈、谁进哪个队列、谁先被调度”。多写几个带 setTimeout 和 Promise 的小例子跑一跑,节奏就自然了。