[JS杂谈(一)][Promise v8源码]群里Promise面试题

吐槽君 分类:javascript

群里看到,不懂就查,查不懂就对线源码。

题目

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

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

输出

1 2 3 4 5 6

逻辑

首先设1.then(() => { console.log(1) })这一个微任务,

其他同理。

一开始执行完两个Promise.resolve()以后:

microTask: `0 1`
 

Promise.resolve(4)返回一个状态为fulfilledPromise此时按逻辑应该把4加入到微任务中了,

但是在then中返回fulfilled状态的Promise的话,

Promise内部会将返回的Promisethen方法执行放入到微任务队列中执行。

microTask: 1 Promise.resolve(4).then
microTask: Promise.resolve(4).then 2
 

then内部因为Promise.resolve(4)状态已经是fulfilled了,

又申请了个微任务为了让0同步Promise.resolve(4).thenfulfilled的状态,

因为需要等Promise.resolve(4).then内部暴露的Promise初始化完毕后再执行,

所以需要一个微任务等待。

(实际为:Promise.resolve(4).then返回的Promise需要一个微任务进行同步Promise.resolve(4)返回的Promisefulfilled状态,在同步时顺便也把外部0返回的Promise一并同步了)

注:0返回的Promise是同步Promise.resolve(4).then的状态

microTask: 2 同步状态
microTask: 同步状态 3
microTask: 3 4
最后输出: 1 2 3 4 5 6
 

证明 Then方法会放入微队列中

0修改为

.then(() => {
  console.log(0)
    // return Promise.resolve(4)
    return {
      then(resolve) {
      console.log('then')
      resolve(4)
    },
  }
})
 
输出:
0
1
then
2
res:  4
3
5
6
 

可以看到then方法是放入了微任务队列中的。

这个then方法中resolve执行直接同步fulfilled状态到0返回的Promise状态上,因此执行完then方法下一个微任务就是输出res了。

源码

我翻找了v84.3.65版本还未使用v8 Torque(tq v8内部语言)或C实现的promise.js的源码,在5版本以后就使用C实现了,最新版本使用tq实现。

通过看源码可知Promise.resovle(x) 相当于new Promise(rs => rs(x)),两者行为一致,有兴趣可以试试。

源码地址

deferred

首先介绍一下deferreddeferred指内部为了链式调用创建的对象,在then方法被调用时返回的就是deferredPromisedeferred对象也是个Promise(废话,不这样咋链式

据上面源码可知Promise.then/返回Thenable都会创建这个对象。据我感觉最新返回Thenable并不会创建这个对象,直接使用外部的deferred对象

function PromiseDeferred() {
  if (this === $Promise) {
    // Optimized case, avoid extra closure.
    var promise = PromiseInit(new $Promise(promiseRaw));
    return {
      promise: promise,
      resolve: function(x) { PromiseResolve(promise, x) },
      reject: function(r) { PromiseReject(promise, r) }
    };
  } else {
    var result = {};
      result.promise = new this(function(resolve, reject) {
      result.resolve = resolve;
      result.reject = reject;
    })
    return result;
  }
}
 

理解

=>为返回,->为步骤,deferred Object简称为deferred

注:之后的"同步状态"微任务只针对非pending状态

then(onResolve) => deferred
	-> onResolve => Promise(fulfilled)
	-> Promise(fulfilled).then(deferred.resolve, deferred.reject)
	-> 放入一个微任务"同步状态"到外部 deferred 上
	-> 以便链式调用
then(onResolve) => deferred
	-> onResolve => Thenable
	-> deferred.resolve(Thenable)
  -> then(onResolve).then(onResolve2, onReject2)
  	-> deferred.promise.then(onResolve2, onReject2)
  		-> 不调用这个 .then 据我理解是不会将 Thenable 对象转换成 defeerred 的
  	-> deferred.promise PromiseState is fulfilled
  	-> 执行 then 方法,onResolve2 在内部会被包装因此
      -> 执行中 Thenable 转换成 thenDeferred 时同步执行 then 方法
        -> 根据 then 方法执行后决定 thenDeferred.promise 状态
      -> thenDeferred.promise.then(onResolve, onReject)
        -> 挂载 deferred.promise.then 的参数到 thenDeferr.promise.then 上
 

返回Thenable对象:

Promise.resolve()
  .then(() => {
    console.log(0)
    return {
      then(resolve) { // 在4.x源码中这儿是同步执行
        console.log('then')
        resolve(4)
      },
    }
  })
  .then(res => {
    console.log('res: ', res)
  })
相当于
Promise.resolve()
  .then(() => {
    console.log(0)
  })
  .then(() => { // 在4.x源码中这儿是同步执行
    console.log('then')
    return 4
  })
  .then(res => {
    console.log('res: ', res)
  })
 

这是在4.3.65版本的实现逻辑(我的理解,有误您对

源码结论

返回Promise都是需要一个微任务,但是Thenable又不需要,但是这是4.多的代码实现,之后代码改动很大,不过思路大体一致。(应该吧,我看不懂后面的了太菜了

推测

因为这是4.3.65的代码了,所以不符合之前推理的逻辑也正常,虽然这个版本的代码已经挺优雅了,但是可能为了更优雅的实现,Thenable/Promisethen要一视同仁,也得放进微任务中执行,没必要做特殊处理,所以两个的then方法都进入了微任务队列中处理。(我寻思挺合理的

其次我的源码理解的返回Thenable对象的promise后面得再跟一个then方法才会执行Thenable.then,所以需要改动并统一行为。(可能是我理解错误?

那么为什么在目前的版本就是返回Promise是要两个微任务,而Thenable还是一个

讲道理

据我推测在之后版本中,只要有Then都会放入微任务队列执行一下,如果是Promise

同步状态其实相当于往一个promisethen方法传入(deferred.resolve, deferred.reject)简称这个行为为同步d

不讨论rejected状态(或者说和 fulfilled状态差不多

PromiseState is fulfilled/Thenable Object:
	设 Promise.resolve().then() 返回的 deferred 为 d
	Promise.resolve().then(() => Promise.resolve(4))
		-> Promise.resolve(4) => Promise(fulfilled) 且非 deferred
		-> 内部处理
			-> 有 then 方法进行劫持放进微任务队列中执行
			-> 由于 then 方法未传入参数, 默认为 onResolve = x => x
			-> 即 Promise(fulfilled).then(x => x)
			-> pending 状态需要同步
			-> Promise(fulfilled).then(x => x).then(同步d)
		-> 以上是按照 4.x 逻辑来的,不过 then 方法变成异步罢了
	Promise.resolve().then(() => ({ then(rs) { rs(4) }})
		-> Thenable 在微任务被调用 then 方法时内部会传入 d.resolve, d.reject 来直接同步外部,
		-> 因此只需要一个微任务执行 then 方法即可
新的内部实现应该比这优雅,但是可惜我看不懂
Promise.resolve(4).then(res => res) -> PromiseState is pending:
	将当前的promise.then中放入同步状态微任务,
	以便可以同步状态到deferred对象上
	比如:
    Promise.resolve().then(() => Promise.resolve(4).then(res => res))
    Promise.resolve().then() 返回的是 deferred(d)
    Promise.resolve(4).then(res => res) 返回的也是 deferred(thenD)
    因为需要同步状态,因此内部处理挂载上去
    Promise.resolve(4).then(res => res).then(同步d)
    以便d能及时更新状态
 

根据

前面代码改成

Promise.resolve()
  .then(() => {
    console.log(0)
    return Promise.resolve(4).then(res => res) // pending 状态挂载同步状态微任务
  }).then(res => {
    console.log('res: ', res)
  })
 

输出:0 1 2 3 4 5 6

结语

一时兴起,大胆猜测,人菜想写,如有错误,轻骂。

内部实现写的好导致的结果,我反正信了。(doge

回复

我来回复
  • 暂无回复内容