大家好,想了解一下 JavaScript 的事件循环机制。它在单线程环境下如何调度宏任务和微任务?执行栈、任务队列和渲染阶段之间的交互细节是什么?请结合示例解释其工作流程,并谈谈在实际开发中需要注意的坑。
JavaScript 中的事件循环(Event Loop)到底是如何运行的?
👁️ 136 görüntüleme💬 3 cevap❤️ 0 beğeni
3 Cevap
我很好奇,在执行完所有微任务后,是否会立刻进入渲染阶段?如果有多个微任务,它们的执行顺序是如何严格保证的?
在 JavaScript 的事件循环里,执行顺序可以简化为以下几步:先执行调用栈(执行栈)中的同步代码,随后进入 **微任务队列**(micro‑task queue),把所有已就绪的微任务(Promise 的 then/catch、MutationObserver、queueMicrotask)依次执行完;只有在微任务全部清空后,才会从 **宏任务队列**(macro‑task queue)中取出一个宏任务(比如 setTimeout、setInterval、I/O 回调、UI 事件)进入调用栈继续运行。渲染阶段(repaint/reflow)则在微任务执行完且即将进入下一个宏任务前触发,这也是为什么在微任务里改变 DOM 常常看不到即时渲染的原因。
```js
console.log('script start');
setTimeout(() => console.log('macro task'), 0);
Promise.resolve()
.then(() => console.log('micro task 1'))
.then(() => console.log('micro task 2'));
console.log('script end');
```
运行结果依次是:`script start → script end → micro task 1 → micro task 2 → macro task`。从我的前端项目经验来看,最容易踩的坑是误以为 `setTimeout(..., 0)` 能立刻执行,实际上它会被推到下一个宏任务循环,而所有同步代码和微任务都会先跑完;再比如在 `async/await` 中忘记捕获异常,异常会转成微任务的 reject,导致后面的代码意外提前结束。另外,频繁在微任务里进行大量计算会阻塞渲染,页面卡顿。实战中,我通常会把耗时的逻辑放到宏任务或 Web Worker 中,只有需要确保顺序的短小操作才用微任务,这样既能保持 UI 的流畅,又能避免意外的执行顺序错误。
I'm curious—when a macrotask schedules a Promise that resolves immediately, does the browser render after the microtask queue is drained before the next macrotask, or does it wait until the original macrotask finishes?