JS中Event Loop与Promise场景下MicroTask/MacroTask队列执行顺序疑问
问题核心根源
你推导输出时的最大错误,是完全搞错了Promise的then回调进入微任务队列的触发条件,才会得出和实际运行不符的结论。
真实执行流程逐行拆解
- 第一阶段:从头到尾跑完全部同步主线程代码
- 定义
wait函数:纯内存操作,不触发任何队列入队。 - 走到
wait()这一行:
- 调用setTimeout(resolve, undefined),把「执行resolve变更Promise状态」这个任务丢进宏任务队列。此时wait返回的Promise是pending状态,根本没决议。
- 给这个pending的Promise挂上then(() => console.log(4)):这个回调现在既不在宏任务队列,也不在微任务队列,它只是存在Promise的内部回调存储结构里。只有当Promise从pending变成resolved的那一刻,对应的then回调才会被推进微任务队列——这是你推导里最关键的错误点。 - 走到
Promise.resolve().then(() => console.log(2)).then(() => console.log(3))这一行:
-Promise.resolve()是直接生成一个已经决议完成的Promise,它绑定的第一个then回调log(2),会立刻被推到微任务队列里。
- 第二个链式then的回调log(3)同样不会立刻入队:它挂在第一个then返回的全新Promise上,这个新Promise现在还是pending状态,得等log(2)执行完、状态变更了,它才有资格进微任务队列。 - 走到
console.log(1):同步代码跑完,控制台打出第一个输出1。 - 第二阶段:同步代码清空后,事件循环开始调度任务
- 事件循环的固定规则是:只要当前微任务队列有任务,就先把所有微任务跑完,再去拿宏任务执行。现在微任务队列里只有
log(2):
- 执行log(2),打出2;这时候第一个then对应的新Promise因为回调执行完成,状态变成resolved,所以log(3)的回调被推到微任务队尾。
- 继续清微任务,拿出log(3)执行,打出3。此时微任务队列已经彻底空了。 - 微任务清空后,事件循环去取最早的一个宏任务执行:就是之前
setTimeout注册的那个resolve回调。
- 执行这个resolve,把wait对应的Promise从pending改成resolved,之前注册的log(4)回调这时候才被推进微任务队列。
- 按照规则,执行完宏任务必须把所有微任务清空,所以拿出log(4)执行,打出4。 - 到这里所有队列都空了,最终输出顺序就是1 → 2 → 3 → 4。
你之前的推导错在哪
- 错把「给pending状态的Promise注册then回调」当成了「回调入队微任务」:实际上只要Promise还没决议,注册的回调就只是存着,根本不会进队列。你之前以为
log(4)在同步阶段就进了微任务队,实际上它要等定时器触发、Promise被resolve之后才会入队。 - 错把「链式then的回调」当成在同步阶段就入队:链式调用的每一层then都对应一个独立的新Promise,后一层的回调入队,必须等前一层的回调执行完成、对应的Promise状态变更才行。
- 你之前脑补的队列顺序是「宏任务存着setTimeout的resolve;微任务顺序是log4 → log2 → log3」,才会推导出1、4、2、3的结果,实际的入队时机和顺序根本不是这样。
内容的提问来源于stack exchange,提问作者ghostrider
相关产品推荐
相关产品推荐

