[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 6java

邏輯

首先設1.then(() => { console.log(1) })這一個微任務,promise

其餘同理。markdown

一開始執行完兩個Promise.resolve()之後:異步

microTask: `0 1`
複製代碼

Promise.resolve(4)返回一個狀態爲fulfilledPromise此時按邏輯應該把4加入到微任務中了,oop

可是在then中返回fulfilled狀態的Promise的話,ui

Promise內部會將返回的Promisethen方法執行放入到微任務隊列中執行。this

microTask: 1 Promise.resolve(4).then
microTask: Promise.resolve(4).then 2
複製代碼

then內部由於Promise.resolve(4)狀態已是fulfilled了,google

又申請了個微任務爲了讓0同步Promise.resolve(4).thenfulfilled的狀態,spa

由於須要等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

相關文章
相關標籤/搜索