Promise源碼解析(一)

Promise很復(fù)雜..恩..原來覺得不就是詞法作用域嗎, 原來覺得詞法作用域不就是調(diào)用時(shí)的堆棧是定義時(shí)的嘛 結(jié)果...一個(gè)簡單概念被玩成了這樣..

promise.js源碼從https://blog.csdn.net/qq_22844483/article/details/73655738

所得 謝謝原作者(未申請轉(zhuǎn)載 啊哈) 原本他的意思是從0寫一個(gè)promise 但是promise的鏈?zhǔn)秸{(diào)用部分看著暈暈的 為什么呢 因?yàn)檎騺砜紤]像我這種菜鳥想不通啊 也罷 那就逆向來想吧 跟一下堆棧 沒成想 兩個(gè)then就20多個(gè)堆棧 一步一步跟我來 反向完了來正向 非把它搞定不可

使用的promise.js是簡版 只有resolve而沒有其他實(shí)現(xiàn)(包括.all/reject等)

先上一段使用代碼 兩個(gè).then

![圖片描述](//img.mukewang.com/5be113d10001170c08070626.jpg)

在最后的console.log打斷點(diǎn) 看到的堆棧是這樣的

![圖片描述](//img.mukewang.com/5be113e70001a3a706980824.jpg)

二十幾個(gè)啊 咳咳 看看多少同名的玩意....

在promise.js中加了一些有意義的輸出 控制臺(tái)是這樣的(圖三)

![圖片描述](//img.mukewang.com/5be1140c000132e812790680.jpg)

這里聲明:

①②③④⑤⑥⑦⑧⑨⑩...為標(biāo)記 可以通過標(biāo)記符在很多行的文字解釋和代碼之間查找 沒辦法 promise里各種同名不同棧的函數(shù)..

上promise.js源碼(使用的是https://blog.csdn.net/qq_22844483/article/details/73655738 里的部分代碼 轉(zhuǎn)載未申請 好開心)

<pre>

var i = 0;

function Promisee(fn) {

? ? var state = 'pending',

? ? ? ? value = null,

? ? ? ? callbacks = [],

? ? ? ? aa = ++i; ②

? Promisee.ii = i, ①

? ? this.then = function thenFun(onFulfilled) {

? ? ? ? return new Promisee(function resolveFuc(resolve) {

? ? ? ? ? ? handle({

? ? ? ? ? ? ? ? onFulfilled: onFulfilled || null,

? ? ? ? ? ? ? ? resolve: resolve

? ? ? ? ? ? });

? ? ? ? });

? ? };

? ? function handle(callback) {

? ? ? ? if (state === 'pending') {

? ? ? ? ? ? callbacks.push(callback);

? ? ? ? ? ? console.log('狀態(tài)是pending',"這是Promisee第",handle.caller.caller.ii,"次執(zhí)行時(shí)執(zhí)行的handle方法 當(dāng)然 handle是在Promisee第", aa,"次定義的");//說明調(diào)用handle的是this.then中的Promisee

? ? ? ? ? ? console.log('push', callbacks);

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? //如果then中沒有傳遞任何東西

? ? ? ? if(!callback.onFulfilled) {

? ? ? ? ? ? callback.resolve(value);

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? console.log('狀態(tài)是fulfilled','這是第',aa,'個(gè)Promisee里的handle');

? ? ? ? var ret = callback.onFulfilled(value); ⑨

? ? ? ? callback.resolve(ret);

? ? }

? ? function resolve(newValue) {

? ? ? ? console.log('這是第',aa,'個(gè)Promisee里的resolve');

? ? ? ? if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {

? ? ? ? ? ? var then = newValue.then;

? ? ? ? ? ? if (typeof then === 'function') {

? ? ? ? ? ? ? ? then.call(newValue, resolve);

? ? ? ? ? ? ? ? return;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? if(newValue == undefined){

? ? ? ? ? ? console.log('\tvalue等于undefined了 還是在resolve內(nèi)執(zhí)行');

? ? ? ? ? ? if(callbacks.length == 0)

? ? ? ? ? ? ? ? console.log('\t啥都木有');

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? state = 'fulfilled';

? ? ? ? value = newValue;

? ? ? ? setTimeout(function setTimeoutFuc() {

? ? ? ? ? ? callbacks.forEach(function (callback) {

? ? ? ? ? ? console.log('pop', callbacks);

? ? ? ? ? ? handle(callback);

? ? ? ? ? ? });

? ? ? ? }, 0);

? ? }

? ? fn(resolve); ⑥

}

</pre>

對照執(zhí)行代碼看下

<pre>

var a = function(){

return new Promisee(function(resolve, reject){

setTimeout(function(){

resolve(1);

},100);

})

}

a().then(function(num){ ③

return new Promisee(function(resolve){

setTimeout(function(){

console.log('這是第二個(gè)setTimeout方法 已經(jīng)開始執(zhí)行');

resolve(num*3);

},100)

})

})

.then(function(num2){ ⑤

console.log('num2',num2);

})

</pre>

Promise.js源碼中很重要的一條語句就是最后的fn(resolve) [標(biāo)記6] 這條語句可以讓帶有參數(shù)的Promise在實(shí)例化時(shí)自動(dòng)執(zhí)行他的參數(shù) 同時(shí)把resolve傳入 注意 這里的resolve如果是then的參數(shù)的參數(shù) 那往往是Promise的上層堆棧的resolve 這個(gè)可以從圖三里很好的看出來 而最重要的所謂反轉(zhuǎn)的反轉(zhuǎn)的實(shí)現(xiàn) 就在這里! 通過handle函數(shù) 利用詞法作用域 將本層堆棧的對象push到上層callbacks中 以實(shí)現(xiàn)不同promise的通信

[標(biāo)記1]這里是我自己標(biāo)記的針對不同promise做的ii屬性 這樣可以在函數(shù)調(diào)用時(shí) 查看它是在哪個(gè)promise調(diào)用的

[標(biāo)記2]這個(gè)就有趣了 可以用來跟蹤所謂通過在函數(shù)promise中添加私有屬性i 在調(diào)用resolve和handle時(shí) 可以同時(shí)打印resolve和handle所在詞法作用域的i屬性 從而顯示這兩個(gè)函數(shù)是哪個(gè)

堆棧調(diào)用的^^

開始看代碼(圖三結(jié)合圖二)

首先[標(biāo)記3] a()執(zhí)行返回promise[這是第一個(gè)promise] 繼續(xù)調(diào)用.then 會(huì)繼續(xù)返回promise[第二個(gè)promise]同時(shí)在then中的function會(huì)作為onFulfilled作為后續(xù)處理 其實(shí)就是通過第二個(gè)promise的handle方法(這個(gè)handle其實(shí)是第一個(gè)promise的) 將onFulfilled和第二個(gè)promise的resolve push到第一個(gè)promise的callbacks中(這里很重要!!!!!!做標(biāo)記④ handle沒有傳參 所以是從上級(jí)的堆棧調(diào)用 而resolve是傳參了 那直接從函數(shù)作用域調(diào)用 故是屬于當(dāng)前promise的resolve) 從圖三的第一行可以看到

![圖片描述](//img.mukewang.com/5be1148d000156a912700119.jpg)

代碼往下走 到達(dá)第二個(gè)then [標(biāo)記5] then返回一個(gè)promise(第三個(gè)promise) 同時(shí)then的參數(shù)同樣作為onFulfilled 和 第三個(gè)promise的resolve 進(jìn)入handle函數(shù)push到第二個(gè)promise中 從圖三的第三四行可以看到

![圖片描述](//img.mukewang.com/5be1149f0001a95608890072.jpg)

代碼往下走 代碼沒了..

回過頭 在剛剛的a()執(zhí)行時(shí) 不僅返回了promise 而且通過promise的最后一行代碼fn(resolve)[標(biāo)記6]執(zhí)行了

<pre>

function(resolve, reject){

? ? ? ? ? ? setTimeout(function(){

? ? ? ? ? ? ? ? resolve(1);

? ? ? ? ? ? },100);

</pre>

這塊的代碼 這個(gè)函數(shù)的執(zhí)行 會(huì)執(zhí)行setTimeout 進(jìn)行隊(duì)列排隊(duì)(意思就是如果有其他代碼執(zhí)行就先執(zhí)行下面的代碼 如果下面的代碼有setTimeout就接著這個(gè)setTimeout排隊(duì) 如果沒有其他代碼執(zhí)行了就開始從頭執(zhí)行setTimeout) 然而在執(zhí)行完兩個(gè)then后 回過頭來開始要執(zhí)行這個(gè)setTimeout了 因?yàn)槠渌a都執(zhí)行完了 promise.js里面的不是執(zhí)行代碼 圖一的才是 ok

好的 這個(gè)倒序整的..

現(xiàn)在setTimeout執(zhí)行了 等待100毫秒后(這個(gè)100毫秒其實(shí)是從注冊了setTimeout就開始算起的) 開始執(zhí)行resolve(1); 這個(gè)resolve是第幾個(gè)promise的resolove? 第一個(gè)! 因?yàn)橐簧蟻砭蛨?zhí)行"a()" 而a里面就是很單純的將promise.js里倒數(shù)27行的resolve傳到了最后的fn(resolve)里(或者說最后的fn(resolve)里的resolve就是promise.js里倒數(shù)第27行的resolve) ok? 真是 太單純了 再也找不到這么單純的了 好難的 555.. 從圖三的第5行也可以看到? ? ? ? ?

![圖片描述](//img.mukewang.com/5be114c6000107fc08880040.jpg)

現(xiàn)在開始執(zhí)行resolve了 也就是下面的這塊代碼 在上面的代碼塊中可以找到

<pre>

? ? ? ? state = 'fulfilled';

? ? ? ? value = newValue;

? ? ? ? setTimeout(function setTimeoutFuc() {

? ? ? ? ? ? callbacks.forEach(function (callback) {

? ? ? ? ? ? ? ? console.log('pop', callbacks);? ? //? <<== 這里很有愛

? ? ? ? ? ? ? ? handle(callback);

? ? ? ? ? ? });

? ? ? ? }, 0);

</pre>

狀態(tài)(state)被改變成了fulfilled 然后開始setTimeout隊(duì)列 問題現(xiàn)在其他代碼都執(zhí)行完了 setTimeout直接開始執(zhí)行了 這里面的callbacks是第幾個(gè)promise的呢 答案是第一個(gè) 因?yàn)閞esolve是第一個(gè)promise的

而第一個(gè)promise的resolve只能找到第一個(gè)promise里的數(shù)據(jù) 如果是第二個(gè)promise的resolve 或者h(yuǎn)andle就可以訪問第二個(gè)promise或者第一個(gè)promise的數(shù)據(jù) 這是作用域的概念 ok

至于callbacks里面的數(shù)據(jù) 實(shí)際上是第一個(gè)then里面的handle push到第一個(gè)promise的callbacks里面的數(shù)據(jù) 由于在promise.js里寫了"console.log('pop', callbacks);"這條語句 所以控制臺(tái)的第六行顯示

![圖片描述](//img.mukewang.com/5be114dc00013d9108880028.jpg)

然后 handle開始處理這個(gè)callback 也就是一個(gè)對象啦

(((((((((((((((((((((((((((各單位注意 高潮開始了))))))))))))))))))))))))))))))

進(jìn)入handle 現(xiàn)在的堆棧是第一個(gè)promise 而之前進(jìn)行push時(shí)[標(biāo)記4]handle用的是第一個(gè)promise的handle 因?yàn)樽鳛閠hen的參數(shù)中的handle也只能用第一個(gè)promise的傳入啊 handle代碼如下

<pre>

? ? function handle(callback) {

? ? ? ? if (state === 'pending') {

? ? ? ? ? ? callbacks.push(callback);

? ? ? ? ? ? console.log('狀態(tài)是pending',"這是Promisee第",handle.caller.caller.ii,"次執(zhí)行時(shí)執(zhí)行的handle方法 當(dāng)然 handle是在Promisee第", aa,"次定義的");//說明調(diào)用handle的是this.then中的Promisee

? ? ? ? ? ? console.log('push', callbacks);

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? //如果then中沒有傳遞任何東西

? ? ? ? if(!callback.onFulfilled) {

? ? ? ? ? ? callback.resolve(value);

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? console.log('狀態(tài)是fulfilled','這是第',aa,'個(gè)Promisee里的handle');

? ? ? ? var ret = callback.onFulfilled(value);

? ? ? ? callback.resolve(ret);

? ? }

</pre>

現(xiàn)在進(jìn)入(第一個(gè)promise的)handle函數(shù) 由于狀態(tài)(第一個(gè)promise的state)已經(jīng)改為fulfilled 故直接執(zhí)行callback.onFulfilled(value); value即是第一個(gè)執(zhí)行的resolve傳入的參數(shù)1 onFulfilled是then傳入的函數(shù)參數(shù)

function(num){

return new Promisee(function(resolve){

setTimeout(function(){ ⑧

console.log('這是第二個(gè)setTimeout方法 已經(jīng)開始執(zhí)行');

resolve(num*3);

},100)

})

}

執(zhí)行后 返回第四個(gè)promise 注意 此時(shí)會(huì)同時(shí)執(zhí)行此promise的參數(shù) 可參考[標(biāo)記6] 執(zhí)行setTimeout方法 結(jié)果就是列入隊(duì)列 此時(shí)setTimeout內(nèi)的的resolve為第四個(gè)promise的resolve

下一句非常重要

`

callback.resolve(ret);

`

這個(gè)callback里的resolve是第幾個(gè)promise的resolve? 看[標(biāo)記4] 是第二個(gè)promise的resolve!看真相圖

![圖片描述](//img.mukewang.com/5be387f50001a14105060042.jpg)

而ret是第四個(gè)promise 好的 開始愉快的執(zhí)行resolve吧

<pre>

function resolve(newValue) {

? ? ? ? console.log('這是第',aa,'個(gè)Promisee里的resolve');

? ? ? ? if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {

? ? ? ? ? ? var then = newValue.then;

? ? ? ? ? ? if (typeof then === 'function') {

? ? ? ? ? ? ? ? then.call(newValue, resolve);

? ? ? ? ? ? ? ? return; ⑦

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? if(newValue == undefined){

? ? ? ? ? ? console.log('\tvalue等于undefined了 還是在resolve內(nèi)執(zhí)行');

? ? ? ? ? ? if(callbacks.length == 0)

? ? ? ? ? ? ? ? console.log('\t啥都木有');

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? state = 'fulfilled';

? ? ? ? value = newValue;

? ? ? ? setTimeout(function setTimeoutFuc() {

? ? ? ? ? ? callbacks.forEach(function (callback) {

? ? ? ? ? ? console.log('pop', callbacks);

? ? ? ? ? ? handle(callback);

? ? ? ? ? ? });

? ? ? ? }, 0);

? ? }

</pre>

由于ret 也就是這里傳進(jìn)來的newValue是一個(gè)promise 進(jìn)入第一個(gè)if 來回來去執(zhí)行到了

`then.call(newValue, resolve);`

言外之意是 "我要執(zhí)行第四個(gè)promise的then方法哦 而且是把第二個(gè)resolve傳進(jìn)去作為onFulfilled哦"

這句代碼實(shí)在是太經(jīng)典了 因?yàn)橹髸?huì)重用第二個(gè)resolve!

好的 then執(zhí)行了 返回了第五個(gè)promise!

![圖片描述](//img.mukewang.com/5be38a72000185c309750023.jpg)

<pre>

this.then = function thenFun(onFulfilled) {

? ? ? ? return new Promisee(function resolveFuc(resolve) {

? ? ? ? ? ? handle({

? ? ? ? ? ? ? ? onFulfilled: onFulfilled || null,

? ? ? ? ? ? ? ? resolve: resolve

? ? ? ? ? ? });

? ? ? ? });

? ? };

</pre>①②③④⑤⑥⑦⑧⑨⑩

通過第四個(gè)promise的handle(不要問我為什么是第四個(gè) 因?yàn)槊總€(gè)then的參數(shù)調(diào)用都只能通過作用于向上找到上級(jí)promise的handle啊 因?yàn)閰?shù)沒傳handle啊 ( ˇ?ˇ )) 把第二個(gè)resolve(即這里的onFulfilled)和第五個(gè)resolve(第五個(gè) wtf!)push到了第四個(gè)promise中

注意上面的[標(biāo)記7] 這里有個(gè)return 按時(shí)一切的push到此結(jié)束了...

回到[標(biāo)記8]的setTimeout 開始執(zhí)行隊(duì)列

`resolve(num*3)`

第四個(gè)resolve開始執(zhí)行 通過閉包向上找到num 也就是[標(biāo)記9]的value

![圖片描述](//img.mukewang.com/5be38c290001d3e506450031.jpg)

此時(shí)的數(shù)字已經(jīng)通過num*3 也就是1*3 得到了數(shù)字3

然后 把第四個(gè)promise中的callbacks逐個(gè)用handle處理了 callbacks里有什么 當(dāng)然是上面幾行里剛剛push的第二個(gè)promise的resolve和第五個(gè)promise的resolve

執(zhí)行第二個(gè)promise的resolve:

`var ret = callback.onFulfilled(value);`

由于resolve里是一個(gè)setTimeout 這樣開始隊(duì)列

再執(zhí)行下一句

`callback.resolve(ret);`

這個(gè)resolve是第五個(gè)resolve 開始執(zhí)行 可是第五個(gè)promise里面啥也木有

回頭開始執(zhí)行隊(duì)列

<pre>

setTimeout(function setTimeoutFuc() {

? ? ? ? ? ? callbacks.forEach(function (callback) {

? ? ? ? ? ? console.log('pop', callbacks);

? ? ? ? ? ? handle(callback);

? ? ? ? ? ? });

? ? ? ? }, 0);

</pre>

第二個(gè)resolve講第二個(gè)promise的callbacks數(shù)組 也就是代碼階段第二個(gè)then執(zhí)行push的函數(shù) 調(diào)出來執(zhí)行

<pre>

.then(function(num2){

console.log('num2',num2);

})

</pre>

重復(fù)的handle代碼我就不再貼了

兩句重要的如下

`var ret = callback.onFulfilled(value);

? ? ? ? callback.resolve(ret);`

第一句是把

`function(num2){

console.log('num2',num2);

}`

執(zhí)行 此處的value就是剛才(num*3)得到的 通過閉包獲得

這樣控制臺(tái)打印出'num2' 3

![圖片描述](//img.mukewang.com/5be390450001233806290029.jpg)

還沒有結(jié)束 各位老板

還有一句沒有執(zhí)行完

`callback.resolve(ret)`

此時(shí)ret是console.log的返回值 即undefined 即使進(jìn)入resolve也是沒有意義的 因?yàn)榈谌齻€(gè)promise里沒有壓入如何的對象(即沒有執(zhí)行handle) 第三個(gè)promise是在哪生成的呢? 大家想想 答案就是執(zhí)行代碼的第二個(gè)then 因?yàn)閠hen后沒有then了 所以也就沒有執(zhí)行那個(gè)沒有的then里面的handle 也就是說如果繼續(xù)有then就繼續(xù)handle繼續(xù)push繼續(xù)扯 扯 扯 扯到世界末日( ⊙ o ⊙ )啊!

![圖片描述](//img.mukewang.com/5be3912e000133d306650099.jpg)

可以了 完結(jié)撒花.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這個(gè)很早以前寫的,今天看群里有人問關(guān)于promise的問題,在這里重新發(fā)一下。偷懶的同學(xué)可以直接拉到最后有完整的代...
    grain先森閱讀 3,031評(píng)論 1 9
  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,844評(píng)論 1 56
  • 一、Promise的含義 Promise在JavaScript語言中早有實(shí)現(xiàn),ES6將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法...
    Alex灌湯貓閱讀 889評(píng)論 0 2
  • 本文適用的讀者 本文寫給有一定Promise使用經(jīng)驗(yàn)的人,如果你還沒有使用過Promise,這篇文章可能不適合你,...
    HZ充電大喵閱讀 7,467評(píng)論 6 19
  • title: promise總結(jié) 總結(jié)在前 前言 下文類似 Promise#then、Promise#resolv...
    JyLie閱讀 12,428評(píng)論 1 21

友情鏈接更多精彩內(nèi)容