javaScript事件循环先执行同步代码,再清空微任务队列,最后执行宏任务;例如,promise.then(微任务)在setTimeout(宏任务)前执行,因每次宏任务执行后需清空所有微任务。

javascript的事件循环机制是理解其异步编程模型的核心。很多人知道setTimeout、Promise、async/await能实现异步操作,但不清楚它们执行顺序背后的原理。关键就在于事件循环(Event Loop)如何协调调用栈、任务队列和微任务队列。
JavaScript单线程与异步的矛盾
JavaScript是一门单线程语言,意味着同一时间只能执行一个任务。如果遇到耗时操作(如网络请求、定时器),阻塞主线程会导致页面卡顿。为解决这个问题,JavaScript引入了事件循环机制,通过非阻塞方式处理异步任务。
浏览器或node.js环境提供了Web API(如dom操作、setTimeout、fetch等),这些API在后台执行异步操作,完成后将回调函数推入任务队列,由事件循环决定何时执行。
宏任务与微任务:任务分类
事件循环中,任务分为两类:宏任务(macro Task)和微任务(Micro Task)。它们的执行优先级不同,直接影响代码执行顺序。
立即学习“Java免费学习笔记(深入)”;
常见宏任务包括:
- 整体代码 script
- setTimeout 回调
- setInterval 回调
- I/O 操作
- ui 渲染
常见微任务包括:
每次事件循环迭代中,先执行当前调用栈中的同步代码,然后清空微任务队列(所有可用微任务),再取一个宏任务执行。这个过程不断重复。
执行顺序实例解析
看一个经典例子,理解宏任务与微任务的执行优先级:
console.log(‘start’);
setTimeout(() => console.log(‘timeout’), 0);
Promise.resolve().then(() => console.log(‘promise’));
console.log(‘end’);
输出顺序是:start → end → promise → timeout
原因如下:
- “start” 是同步代码,立即输出
- setTimeout 被放入宏任务队列
- Promise.then 被放入微任务队列
- “end” 是同步代码,立即输出
- 同步代码执行完毕,事件循环检查微任务队列,执行 Promise 回调,输出 “promise”
- 微任务清空后,取出一个宏任务(setTimeout),输出 “timeout”
事件循环完整流程
一次完整的事件循环大致经历以下步骤:
- 执行全局脚本,同步任务入栈并执行
- 遇到异步操作,交由 Web API 处理,完成后根据类型放入对应队列
- 同步代码执行完毕,检查微任务队列,逐个执行直到清空
- 从宏任务队列中取出一个任务执行
- 执行过程中产生的微任务加入微任务队列,本轮循环末尾继续清空
- 重复以上过程
注意:每个宏任务执行完后,都会清空当前所有的微任务,这是理解 async/await 行为的关键。
基本上就这些。掌握事件循环,你就明白了为什么Promise比setTimeout先执行,也就能写出更可靠的异步逻辑。不复杂但容易忽略细节。