JavaScript通过事件循环实现异步,其核心是调用栈、任务队列与微任务队列的协作:同步代码执行后,先清空微任务队列,再执行宏任务;例如console.log(‘1’)、’4’为同步,promise.then为微任务,setTimeout为宏任务,故输出顺序为1→4→3→2;微任务优先级高于宏任务,合理利用可提升性能与响应性。

JavaScript 是单线程语言,但它通过事件循环(Event Loop)实现了异步编程的能力。理解事件循环的关键在于搞清楚调用栈、任务队列、微任务和宏任务之间的协作方式。
事件循环的基本构成
JavaScript 的运行环境包含以下几个核心部分:
- 调用栈(Call Stack):记录当前正在执行的函数调用。
- 堆(Heap):存放对象等动态数据。
- 任务队列(Task Queue):存放宏任务(如 setTimeout、dom 事件)的回调。
- 微任务队列(Microtask Queue):存放 Promise、MutationObserver 等微任务的回调。
事件循环持续检查调用栈是否为空,一旦为空,就从微任务队列中取出第一个任务执行,微任务清空后再取宏任务执行,如此往复。
宏任务与微任务的区别
不同类型的任务在事件循环中的执行优先级不同:
立即学习“Java免费学习笔记(深入)”;
- 宏任务:包括 script 整体代码、setTimeout、setInterval、I/O、ui 渲染等。
- 微任务:包括 Promise.then、queueMicrotask、MutationObserver 回调等。
每次事件循环迭代中,先执行全局脚本(一个宏任务),然后执行所有可用的微任务,再进入下一个宏任务。这意味着微任务总是在当前宏任务结束后立即执行,不会等待下一次循环。
例子说明执行顺序:
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 分批处理。
- 微任务适合用于需要“尽快执行但不立即”的逻辑,比如状态更新后的通知。
- DOM 更新通常在宏任务之间进行,因此想在 DOM 渲染后操作,可使用 requestAnimationFrame 或 setTimeout(() => {}, 0)。
- 注意 Promise 链可能堆积微任务,导致其他任务延迟,需控制链式调用深度。
基本上就这些。掌握事件循环不是为了背流程图,而是为了写出更可控、不易出错的异步代码。它解释了为什么某些回调看似“延迟为0”却仍不立刻执行,也帮助你理解 vue.nextTick 或 react 的批量更新背后的原理。


