You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript Promise .then()的事件循环处理及代码执行步骤解析

这段Promise与setTimeout的事件循环执行流程详解

先明确你理解里的误区:.then回调不会在调用时就加入微任务队列,只有当Promise的状态变为resolved/rejected时,对应的回调才会被推入微任务队列。下面是逐步骤的执行流程:

1. 同步代码执行阶段

  • 执行var p1 = new Promise(...):
    Promise构造函数是同步执行的,里面的setTimeout(function() { resolve(1) }, 100)被调用,这个setTimeout的回调函数会被浏览器定时器模块记录,100ms后被推入宏任务队列等待执行。此时p1的状态为pending。
  • 执行var p2 = new Promise(...):
    同理,构造函数同步执行,内部的setTimeout(function() { resolve(2) }, 0)被调用,回调被定时器模块记录(即使延迟0ms,浏览器也会按宏任务规则处理),之后会被推入宏任务队列。此时p2的状态也是pending。
  • 执行p1.then((value)=>{console.log(value)}):
    因为p1当前是pending状态,这个回调函数会被Promise内部缓存起来,不会进入微任务队列。
  • 执行p2.then((value)=>{console.log(value)}):
    和p1的情况一样,p2处于pending,回调被Promise缓存,不进入微任务队列。

2. 同步代码执行完毕,开始处理宏任务

此时微任务队列为空,事件循环开始处理宏任务队列:

  • 首先,p2的setTimeout延迟时间更短(逻辑上优先处理),它的回调被取出执行:
    调用resolve(2),p2的状态从pending变为resolved。Promise会把之前缓存的p2.then回调推入微任务队列。
  • 当前宏任务执行完毕,立刻清空微任务队列:
    取出console.log(2)执行,控制台输出2,微任务队列被清空。

3. 处理剩余宏任务

等待100ms后,p1的setTimeout回调被推入宏任务队列:

  • 取出p1的setTimeout回调执行:
    调用resolve(1),p1的状态变为resolved,Promise把之前缓存的p1.then回调推入微任务队列。
  • 当前宏任务执行完毕,清空微任务队列:
    取出console.log(1)执行,控制台输出1,微任务队列清空。

关键总结

  • Promise的.then/.catch回调只有在Promise状态确定后,才会被加入微任务队列,pending状态下只会被缓存。
  • 事件循环的核心顺序:同步代码执行 → 清空当前所有微任务 → 取出一个宏任务执行 → 清空当前所有微任务 → 重复此流程。
  • setTimeout的回调属于宏任务,哪怕设置延迟0ms,也必须等同步代码和当前微任务全部处理完才会执行。

内容的提问来源于stack exchange,提问作者Liangtao Hu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 18:22:38