JavaScript中Promise的执行顺序详解
本文深入解析JavaScript中Promise的执行顺序,通过具体代码案例剖析控制台输出1,2,4,3的原因。重点讲解Promise实例化时同步执行、then回调进入微任务队列的机制,以及Promise/A+规范中关于链式调用和回调执行顺序的规定。通过对比不同代码写法,阐明如何正确控制异步任务的执行流。
前言
近期遇到一段关于 Promise 的趣味代码示例。
new Promise((resolve) => {
console.log(1)
resolve()
}).then(() => {
new Promise((resolve) => {
console.log(2)
resolve()
}).then(() => {
console.log(4)
})
}).then(() => {
console.log(3)
})
初次观察这段代码时,直觉可能让人误以为输出结果会是 1, 2, 3, 4,但实际运行结果却出乎意料。

如截图所示,实际的输出顺序为:1, 2, 4, 3。
代码分析
为了弄清为何输出结果是 1, 2, 4, 3,我们需要逐步拆解代码的执行流程。
理解 Promise 机制的关键在于:实例化时传入的回调函数会立即同步执行,而 then 方法中的回调会被推入微任务队列(Microtask Queue),待当前执行栈清空后按顺序执行。队列遵循先进先出原则,先入队的回调优先执行。在上述代码中,共涉及 5 个回调函数。

回调1 对应 Promise 实例化时的构造函数回调,因此会立即执行,此时控制台打印数字 1。随后调用 resolve() 方法,将 Promise 状态从 pending 修改为 fulfilled(若未调用 resolve,状态保持 pending)。

Promise 实例化完成后,紧接着调用第一个 then() 方法,此时 回调2 被放入微任务队列,等待后续执行。
then 方法何时调用?
这里产生了一个关键疑问:第一个 then() 调用后,第二个 then 是否会立即执行?如果立即执行,输出结果应为 1, 2, 3, 4。实际上,第二个 then() 并不会立即执行,即 回调5 不会立即被放入微任务队列。那么,它究竟何时被触发?
此时需要参考 Promise/A+ 规范,重点关注以下条款:
2.2 then 方法 promise 的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected)
2.2.2 如果 onFulfilled 是函数:
- 2.2.2.1 当 promise 处于已处理状态时,该函数必须被调用并将 promise 的值作为第一个参数。
- 2.2.2.2 该函数一定不能在 promise 处于已处理状态之前调用。
- 2.2.2.3 该函数被调用次数不超过一次。
2.2.6 then 可以在同一个 promise 上多次调用。
- 2.2.6.1 如果 promise 处于已处理状态时,所有相应的 onFulfilled 回调必须按照它们对 then 的组织顺序依次调用。
- 2.2.6.2 如果 promise 处于已拒绝状态时,所有相应的 onRejected 回调必须按照它们对 then 的组织顺序依次调用。
2.2.7 then 必须返回一个 promise。
promise1 = new Promise(resolve => resolve())
// promise1 可以多次调用 then
// 且 onFulfilled 回调的执行顺序,按照 .then 的调用顺序执行
promise1.then(onFulfilled1) // 1
promise1.then(onFulfilled2) // 2
promise1.then(onFulfilled3) // 3
// 上面 3 个 onFulfilled,按照 1、2、3 的顺序执行// 调用 .then 方法后,返回一个新的 promise
promise2 = promise1.then(onFulfilled, onRejected);综上所述,调用第一个 then() 方法会返回一个新的 Promise 对象。这一设计旨在支持链式调用,且 then() 内部的 onFulfilled 回调需等待前一个 Promise 状态变更完成后才会执行。
我们对前述代码的调用形式稍作调整,如下所示。
const p1 = new Promise((resolve) => {
console.log(1)
resolve()
})
const p2 = p1.then(() => {
new Promise((resolve) => {
console.log(2)
resolve()
}).then(() => {
console.log(4)
})
})
const p3 = p2.then(() => {
console.log(3)
})p1.then() 返回一个新的 Promise,记为 p2。随后 p2.then() 的回调将在 p1.then() 内部的回调函数执行完毕、即 p2 状态发生改变后才会被触发。
因此,只有 回调2 执行完毕后,才会执行 p2.then()。接下来我们查看 回调2 的具体内容。

在 回调2 中,首先实例化了一个新的 Promise,其构造函数回调为 回调3。该回调立即执行,控制台打印数字 2,随后调用 resolve() 将状态改为 fulfilled,并将 回调4 放入微任务队列。当 回调2 执行结束后,程序继续执行 p2.then(),此时 回调5 被放入微任务队列。
根据微任务队列先进先出的执行规则,先执行 回调4,再执行 回调5。因此,控制台先输出数字 4,随后输出数字 3。
若希望输出结果为 1, 2, 3, 4,可将代码修改为以下形式。
const p1 = new Promise((resolve) => {
console.log(1)
resolve()
})
p1.then(() => {
new Promise((resolve) => {
console.log(2)
resolve()
}).then(() => {
console.log(4)
})
})
p1.then(() => {
console.log(3)
})
依据前述 2.2.6 规则,then 方法可以在同一个 Promise 上多次调用,且 p1 后续的 then 会按照调用顺序直接将其回调放入微任务队列中。
总结
至此,关于 JavaScript 中 Promise 执行顺序的解析完毕。如需了解更多 js Promise 的执行顺序相关内容,建议查阅本站过往文章或继续阅读后续系列文章,感谢大家的支持!
JavaScript怎么准确判断数据类型:typeof、instanceof与Object.prototype.toString
2026-11-05 17:09:21
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
