promise比settimeout先执行,因为微任务队列会在当前宏任务结束后立即被清空,而settimeout回调属于宏任务,必须等待所有微任务执行完毕后才进入下一轮事件循环。

直接说结论:事件循环不是“等 setTimeout 执行完再跑 Promise”,而是每次宏任务结束后,**立刻清空整个微任务队列**,之后才取下一个宏任务;这个“立刻清空”是关键,也是绝大多数面试题出错的根源。
为什么 console.log(1)、Promise.then、setTimeout(0) 输出顺序不是 1→2→3?
因为执行顺序不由“谁写在前面”决定,而由任务类型和事件循环阶段控制:
-
console.log(1)是同步代码,立即执行 -
Promise.resolve().then(...)的回调被推入**当前事件循环的微任务队列**,主线程一空就执行(不等下一个 tick) -
setTimeout(..., 0)的回调被推入**下一个事件循环的宏任务队列**,必须等当前宏任务 + 所有微任务全部跑完才轮到它
所以哪怕 delay 是 0,setTimeout 也永远排在 Promise.then 后面——这不是“延迟不够”,而是规范定义的调度层级。
Promise 构造函数里的 resolve() 是同步还是异步?
构造函数体内的代码(包括 resolve() 调用)是**同步执行**的,但 .then() 回调一定是微任务。
常见误判点:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 写
new Promise(resolve => { console.log('a'); resolve(); console.log('b'); })→ 输出 a、b 立刻出现,说明构造函数同步运行 - 但
.then(() => console.log('c'))不会紧跟着 b 输出,它要等到当前调用栈清空、微任务队列开始执行时才触发 - 如果 resolve 传的是一个 pending Promise(比如
resolve(Promise.resolve(42))),那 .then 仍进微任务队列,但实际执行时机可能受嵌套 Promise 影响
async/await 本质就是 Promise 微任务,但容易漏掉隐式 await
async 函数返回值总是 Promise,await 后面的表达式一旦不是 Promise,会被自动包装成 Promise.resolve(value),然后其后续代码被注册为微任务。
典型陷阱:
-
async function f() { console.log('1'); await 2; console.log('2'); }→ 输出 1、2 是连续的,但“2”不是同步输出,它在微任务中执行 - 如果
f()被调用后,紧接着有Promise.resolve().then(() => console.log('3')),那么“2”和“3”的顺序不确定:取决于 JS 引擎是否把 await 后续代码和这个then放进同一个微任务队列(Chrome 有时会合并,但规范不要求) - 更危险的是
await Promise.resolve().then(...):这里产生了两层微任务,第二层会在第一层执行完后才入队,导致额外延迟
浏览器和 Node.js 的事件循环差异不能靠记忆,得看阶段名
浏览器 Event Loop 没有明确阶段划分,只按“宏任务 → 清空微任务 → 渲染(可选)→ 下一个宏任务”循环;Node.js(v11+)则严格分 6 个阶段,其中 process.nextTick() 和 Promise.then() 都在 **microtask queue**,但它在每个阶段结束后都执行,比浏览器更激进。
真正影响实操的点:
- Node.js 中
process.nextTick()优先级高于Promise.then(),即使写在后面也会先执行 - 浏览器里没有
process.nextTick(),别在前端代码里用 - 写跨平台工具库时,避免依赖
nextTick或特定阶段行为,统一用queueMicrotask()更安全
最常被忽略的其实是渲染时机:微任务清空后,浏览器会检查是否需要重绘。这意味着如果你在微任务里疯狂改 DOM,不会立刻触发多次 layout,但一次微任务链太长,会阻塞渲染——这比 setTimeout 延迟更隐蔽。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










