Promise.resolve 返回 Promise 的执行顺序

分析 return Promise.resolve() 时的微任务执行机制

问题

以下代码的输出结果是什么?

Promise.resolve().then(() => {
  console.log(0)
  return Promise.resolve(4)
}).then((res) => {
  console.log(res)
})

Promise.resolve().then(() => {
  console.log(1)
}).then(() => {
  console.log(2)
}).then(() => {
  console.log(3)
}).then(() => {
  console.log(5)
}).then(() =>{
  console.log(6)
})

解答

输出结果是:0 1 2 3 5 6 4

原因分析

resolvereturn 一个 Promise 对象时,Chrome 内部实现会产生额外的微任务。这与标准的 Promise/A+ 规范存在差异。

对比三种情况:

// 情况1:resolve 一个 Promise
new Promise(resolve => {
    resolve(Promise.resolve(4))
})
.then((res) => {
    console.log(res) // 4
})

// 情况2:return 一个 Promise
Promise.resolve().then(() => {
    return Promise.resolve(4)
})
.then((res) => {
    console.log(res) // 4
})

// 情况3:return 一个普通值
Promise.resolve().then(() => {
    return 4
})
.then((res) => {
    console.log(res) // 4
})

情况 1 和情况 2 虽然最终输出都是数字 4,但在执行过程中会比情况 3 多产生两个微任务。

执行机制

resolvereturn 遇到 Promise 对象时:

  1. 先获取这个 Promise 的值(等待状态变为 fulfilled)
  2. 用微任务包装这个值
  3. 向外传递时再产生一个微任务

因此,return Promise.resolve(4) 可以理解为:

Promise.resolve().then(() => {
    return 4
})
.then() // 额外的微任务1
.then() // 额外的微任务2
.then((res) => {
    console.log(res)
})

题目转化

原题目可以转化为:

Promise.resolve().then(() => {
    console.log(0)
    return 4
})
.then()
.then()
.then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1)
}).then(() => {
    console.log(2)
}).then(() => {
    console.log(3)
}).then(() => {
    console.log(5)
}).then(() =>{
    console.log(6)
})

执行顺序:

  • 第一轮微任务:输出 0 和 1
  • 第二轮微任务:输出 2(第一个 Promise 链的空 then)
  • 第三轮微任务:输出 3(第一个 Promise 链的空 then)
  • 第四轮微任务:输出 5
  • 第五轮微任务:输出 6
  • 第六轮微任务:输出 4

特殊情况

嵌套多层 Promise.resolve() 不会产生额外微任务:

Promise.resolve().then(() => {
    return Promise.resolve(Promise.resolve(Promise.resolve(4)))
})
.then(res => {
    console.log(res) // 和 return Promise.resolve(4) 效果相同
})

但如果 Promise 链本身需要等待,则会累加微任务:

Promise.resolve().then(() => {
    return new Promise(resolve => {
            resolve(4)
    })
    .then(res => {
            return 4.1
    })
    .then(res => {
            return 4.2
    })
})
.then(res => {
    console.log(res) // 需要等待前面的 Promise 链执行完
})

关键点

  • return Promise.resolve(value) 会比 return value 多产生两个微任务
  • 第一个微任务用于包装 Promise 的值,第二个微任务用于向外传递
  • 嵌套的 Promise.resolve() 不会产生额外微任务,因为状态立即确定
  • 这是 Chrome 内部实现的特性,与标准 Promise/A+ 规范有差异