JavaScript异步执行顺序由事件循环严格按“同步→微任务→宏任务”三级调度:同步代码立即执行完毕;随后清空微任务队列(含Promise.then、await后续等),期间新增微任务也即时执行;最后执行一个宏任务(如setTimeout),之后再次清空微任务队列。

JavaScript中多段异步代码的执行顺序,由事件循环(Event Loop)严格按“同步 → 微任务 → 宏任务”三级调度决定,不是看代码书写位置,而是看任务被归类到哪个队列、以及何时入队。
同步代码永远最先跑完
所有直接写的语句(比如 console.log、变量赋值、函数调用)都属于同步任务,它们会按书写顺序立刻执行,直到调用栈清空。这一步不涉及任何队列,是整个事件循环的起点。
微任务紧接同步之后全部执行
同步代码一结束,引擎立刻检查微任务队列,并把当前队列里所有任务依次执行完——中间不插宏任务、不暂停:
- 典型来源:Promise.then/catch/finally 回调、async/await 中 await 后的代码、queueMicrotask()、MutationObserver(浏览器环境)
- 关键特性:执行过程中新产生的微任务(比如在 then 里再写一个 Promise.resolve().then)会被加到当前微任务队列尾部,继续执行,不会等到下一轮
- 优先级体现:哪怕 setTimeout 设为 0,Promise.then 也一定先于它输出
宏任务每次只取一个,且必须等微任务清空后才开始
微任务队列清空后,事件循环才从宏任务队列中取出最先进入的一个执行:
立即学习“Java免费学习笔记(深入)”;
- 常见宏任务:setTimeout/setInterval 回调、DOM 事件(如 click)、fetch 或 XMLHttpRequest 的回调、UI 渲染(浏览器中)、整个 <script> 标签本身(即第一个宏任务)
- 排队逻辑:多个 setTimeout 按注册顺序排队;不同来源的宏任务(如点击事件和定时器)共用一个队列,按触发时间+入队顺序调度
- 执行间隙:每执行完一个宏任务,都会再次清空当前微任务队列——这意味着宏任务内部产生的微任务,会在该宏任务结束后立刻执行,而不是等下一个宏任务
验证顺序靠打点,不是靠猜
实际判断多段异步代码谁先谁后,不能靠肉眼读代码,必须用带标识的 console.log 验证:
- 在关键位置插入类似
console.log('✅ 同步结束')、console.log('? Promise.then')、console.log('⏰ setTimeout')这样的标记 - 运行后观察输出顺序,就能清晰看到事件循环的真实调度路径
- 特别注意:async/await 的 await 表达式之后的代码,本质是被包装进微任务,所以它比同级的 setTimeout 更早执行


















