如何理解Promise中,运行 [[Resolve]](promise, x),所呈现的异步特性

发布于 2022-09-11 17:35:11 字数 2551 浏览 8 评论 0

[[Resolve]](promise, x)

如果 x 为 Promise ,则使 promise 接受 x 的状态 注4:
如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
如果 x 处于执行态,用相同的值执行 promise
如果 x 处于拒绝态,用相同的据因拒绝 promise
如果 x 不为对象或者函数,以 x 为参数执行 promise

以上摘录自Promise/A+规范

如果按照规范中的描述,那么当x=10 或者 x = Promise.resolve(10)时,两者的表现应该是相同的。他会拿出这个为执行态的promise的值,进行一次resolve。这整个过程应该类似于语法糖?但事实好像有点偏差

我们来看一段代码

new Promise((resolve, reject) => {
  let a = new Promise(resolve => {
    resolve(10)
  })
  let b = new Promise(resolve => {
    let c = Promise.resolve(10)
    resolve(c)
  })
  console.log(a)
  console.log(b)
  Promise.resolve(0)
    .then(() => {
      console.log('micro1')
      console.log(b)
    })
    .then(() => {
      console.log('micro2')
      console.log(b)
    })
  setTimeout(() => {
    console.log('marco')
    console.log(b)
  })
})

这是输出

Promise { 10 }
Promise { <pending> }
micro1
Promise { <pending> }
micro2
Promise { 10 }
marco
Promise { 10 }

我们可以看到,当[[Resolve]](promise, x)时,如果X是一个普通值,将会立即同步的更改此Promise状态为执行态Fulfilled.

但是当X是一个状态为执行态的Promise对象时,promise对象的状态没有被立即改变,改变的时间点也非常的奇怪,如果这个我称为状态同步的过程是一个异步的放置在microTask中的任务,那么他应该改变在micro1之前(因为他会被更早的插入microTask之中)。如果他放置到marcoTask,那么他应该发生在运行setTimeout之前。

所以我的问题其实就是如何理解这个过程。

  1. 这个状态同步会放置到microTask中吗
  2. 如果是,它何时被放到microTask之中
  3. 有相关的规范描述这个过程吗

在提出问题的同时,我自己也在积极找寻答案,以下放最新进展

进展

  1. Chrome中实现的应该是ES6 Promise规范,并非Promise/A+规范。ES6 Promise来源于Promise/A+规范。
  2. 以上这个状态同步似乎有官方称呼PromiseResolveThenableJob,这个过程并不会放到microTask中,他会放到名为PromiseJobs的 job queue中。
  3. 好吧,job queue是microTask的别名,这玩意就是放到microTask中执行的。

猜想
根据以上的一些信息,我个人产生了这样的猜想

PromiseResolveThenableJob 分为两步,第一步,首先放一个任务A进microTask中,A的操作是检查目标X的状态,如果状态为执行态,那么继续放入一个任务B到microTask中,B的内容才是语法糖resolve(x的值),如此分为两步添加。能够解释上面的奇怪顺序问题。exp:同步代码运行后microTask中有任务A和Micro1 接下来执行任务A,放置任务B到microTask队尾,执行Micro1,接下来执行任务B,更改Promise状态。

以上还没有详细的规范支撑,只是一个猜想

追问

那这样是不是可以认为

 let b = new Promise(resolve => {
    let c = Promise.resolve(10)
    resolve(c)
  })

可以认为是如下的语法糖

let b = new Promise(resolve => {
  Promise.resolve(10).then(res => {
    resolve(res)
  })
})

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

七度光 2022-09-18 17:35:11

你可能被 console.log 误导了。虽然打印显示 Promise 已经 fulfill 了,但其 onFulfilled 必然会在平台代码执行完了才会开始的(见规范底部注解)。

所以 b 里的 resolve(c) 会在第二轮清理 Promise 队列时才会被处理。

而第二轮中新增的 micro1 会加到栈前新的队列,所以会先于 b 被 fulfilled 。

micro2 已经到第三轮了,这时 b 已经被 fulfilled。

最后帮你列出整个方便理解。

第一次平台代码执行后栈中添加了一条队列

+ a
+ b
+ c
+ Promise.resolve(0)

清理栈,新增的 Promises 被添加到栈上新的队列

+ micro1
- a
b
- c
- Promise.resolve(0)

第二轮清理

+ micro2
- micro1
- b

第三轮

- micro2
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文