Promise的實(shí)現(xiàn)及詳解

排版很不友好,抱歉


/*

* 實(shí)現(xiàn)Promise是根據(jù)Promise規(guī)范來(lái)的:https://promisesaplus.com/

* 規(guī)范很短,所以每句都很重要

* Promise特點(diǎn)

* 1. 狀態(tài)改變后不可再改變(狀態(tài)凝固)

*? ? pending,fullfilled,rejected

*

* 2. then方法(此方法就是最重要的部分)

*? ? 可以鏈?zhǔn)秸{(diào)用

*? ? then方法返回一個(gè)新的promise實(shí)例

*? promise2 = promise.then(f1, f2)

*? ? 當(dāng)promise的狀態(tài)確定的時(shí)候會(huì)執(zhí)行then中的f1或者f2。

*? ? 在f1/f2執(zhí)行之前then就已經(jīng)返回了一個(gè)Promise實(shí)例(promise2)

*? ? promise2的狀態(tài)由f1/f2的返回值確定,以此類推就是鏈?zhǔn)秸{(diào)用

*

* 3. promise創(chuàng)建時(shí)傳入的函數(shù)會(huì)立即執(zhí)行

*/

/*

* 在考慮某些函數(shù)執(zhí)行時(shí)同步還是異步,考慮四種情況,分別會(huì)有什么問(wèn)題

* 假設(shè)new一個(gè)Promise時(shí)傳入的函數(shù)為funA, then中的函數(shù)為funCD

*? 1 funA是同步,funCD為同步

*? 2 funA是同步,funCD為異步 // 為什么最后只能用這種方式

*? 3 funA是異步,funCD為同步

*? 4 funA是異步,funCD為異步

*/

/*

* 問(wèn)題: 1,狀態(tài)是怎么改變的(這是個(gè)好問(wèn)題,是面試官提的。當(dāng)時(shí)我真的沒(méi)答出來(lái))

*? ? ? ? ? 在執(zhí)行resolve,reject函數(shù)時(shí)會(huì)改變狀態(tài)??磖esolve,reject的函數(shù)聲明

*? ? ? 2,new Promise時(shí)傳入的函數(shù)為什么要立即執(zhí)行,then里傳的兩個(gè)函數(shù)為什么要異步執(zhí)行?

*? ? ? ? ? resolve,reject函數(shù)是用戶決定什么時(shí)候執(zhí)行的,同步、異步執(zhí)行都是用戶的行為

*? ? ? ? ? 這里可以好好分析下,信息量很大

*/

/*

* 狀態(tài)確定后挨個(gè)調(diào)用數(shù)組中的方法

* 狀態(tài)是在resolve,reject函數(shù)執(zhí)行時(shí)改變的

*/

function Promise(executor) {

var self = this

/*

* 為什么要?jiǎng)?chuàng)建數(shù)組?

* new出第一個(gè)實(shí)例promise后,可以調(diào)用多次then

* 而此時(shí)promise的狀態(tài)可能還不確定,所以來(lái)個(gè)數(shù)組保存下then中的函數(shù),等到狀態(tài)確定后再執(zhí)行then中的函數(shù)。

* 保存then中的回調(diào)函數(shù),注意查看什么時(shí)候push進(jìn)去的

*/

self.resolvedCallbacks = []

self.rejectedCallbacks = []

// 狀態(tài)

self.status = 'pending'

/*

* resolve中應(yīng)該包含一個(gè)對(duì)象,因?yàn)槊看蝿?chuàng)建一個(gè)實(shí)例

* 彼此間都不會(huì)有影響(這難道不是單例模式么?還是工廠函數(shù)?)

* 狀態(tài)[確定]后挨個(gè)調(diào)用數(shù)組中的方法,必須被當(dāng)做函數(shù)調(diào)用。

* 即調(diào)用時(shí)函數(shù)體內(nèi)的this應(yīng)該是undefined/window

* --------------------------------------------------

* 成功傳值,失敗傳原因

* --------------------------------------------------

* resolve和reject是什么時(shí)候被執(zhí)行的呢?

* 這兩個(gè)函數(shù)是在executor體內(nèi)被執(zhí)行的

* --------------------------------------------------

* 對(duì)于在resolve,reject兩個(gè)函數(shù)中

* 異步執(zhí)行resolvedCallbacks,rejectedCallbacks中函數(shù)問(wèn)題

*

*/

function resolve(value) {

// 測(cè)試用例給我們傳了一個(gè)我自己定義的Promise實(shí)例時(shí)

if (value instanceof Promise) {

value.then(resolve, reject)

return

}

// 這里為什么也要異步

// 因?yàn)閿?shù)組resolvedCallbacks,rejectedCallbacks不能跟resolve同步執(zhí)行。為什么?

setTimeout(function() {

/*

* 這里是為了確保狀態(tài)不可改變,即resolve/reject只執(zhí)行一次

*/

if (self.status === 'pending') {

self.status = 'resolved'

// 保存?zhèn)魅氲膮?shù),promise成功時(shí)的值

self.data = value

var f

// 性能優(yōu)先,使用原始for循環(huán)

for (var i = 0; i < self.resolvedCallbacks.length; i++) {

/*

* 為什么分開(kāi)寫?

* 確保是函數(shù)的調(diào)用,即被調(diào)用時(shí)函數(shù)體內(nèi)的this為undefined/window

* 標(biāo)準(zhǔn)里都有說(shuō)明

*/

f = self.resolvedCallbacks[i]

f(value)

}

}

})

}

function reject(reason) {

// 為什么是異步

setTimeout(function() {

if (self.status === 'pending') {

self.status = 'rejected'

self.data = reason

var f

for (var i = 0; i < self.rejectedCallbacks.length; i++) {

f = self.rejectedCallbacks[i]

f(reason)

}

}

})

}

try {

// promise創(chuàng)建時(shí)傳入的函數(shù)會(huì)立即執(zhí)行,而傳入的函數(shù)不知道有沒(méi)有問(wèn)題.所以要try下

executor(resolve, reject)

} catch(e) {

reject(e)

}

}

/*

* then方法中實(shí)現(xiàn)根據(jù)狀態(tài)調(diào)用不同的函數(shù),

* 并且在調(diào)用回調(diào)前返回一個(gè)新的promise且此promise的狀態(tài)由回調(diào)函數(shù)確定

*? pending -> fulfilled : 調(diào)用onResolved

*? pending -> rejected : 調(diào)用onRejected

*? onRejected\onResolved必須是函數(shù),否則忽略它

*/

Promise.prototype.then = function(onResolved, onRejected) {

var self = this

if (typeof onResolved !== 'function') {

/*

* 為什么要這樣?

* 例如這種情況:p2 = p1.then(null, f2),沒(méi)有傳onResolved函數(shù)

*? p1成功了,應(yīng)該調(diào)用onResolved函數(shù)。

*? 正常情況下p2的狀態(tài)由onResolved確定,而onResolved沒(méi)有東西。

*? 此時(shí)p2的狀態(tài)應(yīng)該由p1決定。

*? 怎么拿到p1的狀態(tài)?在then中成功時(shí)調(diào)用的函數(shù)將p1的狀態(tài)返回就拿到p1的狀態(tài)!

*? 在沒(méi)有返回函數(shù)情況下,將onResolved用一個(gè)函數(shù)代替

* ---------------------------------------------------------------

*? 為什么可以這么寫,我沒(méi)有吃透

*

*? 這個(gè)函數(shù)被執(zhí)行的時(shí)候value就有值了,狀態(tài)改變onResolved(self.data)執(zhí)行

*? 在這種情況下value的值查查self.data

*? self.data是在構(gòu)造函數(shù)里的resolve(value)里被賦值的。所以這里非常的繞,還需要再來(lái)理解消化

*

*/

onResolved = function(value) { return value }

}

if (typeof onRejected !== 'function') {

/*

* 為什么要這樣?

* 例如這種情況:p2 = p1.then(null, f2),沒(méi)有傳onResolved函數(shù)

*? p1失敗,p2也應(yīng)該失敗。

*? 這里throw,后面的try可以catch到。catch到就會(huì)reject(e)

*? 將p1的失敗reason拋出來(lái),p2也就失敗了

*? 具體的過(guò)程跟上面的函數(shù)是一樣的

*/

onRejected = function(reason) { throw reason}

}

/*

* 實(shí)例在三種狀態(tài)下都可以調(diào)用then方法

* 判斷狀態(tài)的三種情況。為了方便

* 在三種情況下

* 我要實(shí)現(xiàn)的是

*/

var promise2

if (self.status === 'resolved') {

/*

* 返回一個(gè)新的promise2,在promise2中調(diào)用onResolved且傳入promise成功時(shí)傳入的值

* 而promise成功時(shí)傳入的值到底是什么,我們是不知道的。

* 這是promise中最復(fù)雜的?。?!

* ----------------------------------------------------------------------

* onResolved返回值x決定了promise2的狀態(tài)

*

*/

promise2 = new Promise(function(resolve, reject) {

// 為什么這里要異步?TODO

setTimeout(function() {

try {

/*

* 這個(gè)data是第一個(gè)promise中傳入的函數(shù)中的resolve攜帶的參數(shù)?。。?!

* onResolved是在then里的,

* 如果onResolved返回的又是Promise實(shí)例x呢,

* --------------------------------------------------------------

* 這次的promise的狀態(tài)是由返回的新的Promise實(shí)例決定

* 而新的實(shí)例的狀態(tài)什么時(shí)候確定呢?我們不用管,只需要在其后掛個(gè)then。

* then中函數(shù)運(yùn)行的時(shí)候就是狀態(tài)確定的時(shí)候

* --------------------------------------------------------------

* 直接resolve是不行的,需要在返回的實(shí)例x中的then里resolve值

*/

var x = onResolved(self.data)

// 非兼容

// if (x instanceof Promise) {

//? // 非簡(jiǎn)化。可以想想了,又是遞歸?

//? // x.then(function(value) {

//? //? resolve(value)

//? // }, function(reason) {

//? //? reject(reason)

//? // })

//? // 簡(jiǎn)化

/*

* 這種簡(jiǎn)化讓人頭疼,resolve,reject是需要傳參的。問(wèn)題來(lái)了,兩個(gè)函數(shù)的參數(shù)是怎么傳進(jìn)去的

* 這里的resolve,reject其實(shí)就是onResolved和onRejected。這種情況又是遞歸了

*/

//? x.then(resolve, reject)

// } else {

//? resolve(x)

// }

// 兼容下

RESOLVE_PROMISE(promise2, x, resolve, reject)

} catch(e) {

reject(e)

}

})

})

}

if (self.status === 'rejected') {

promise2 = new Promise(function(resolve, reject) {

// 為什么這里要異步?TODO

setTimeout(function() {

try {

var x = onRejected(self.data)

// 非兼容,如果x是Bluebird,Q里的promise呢?if就不會(huì)執(zhí)行了

// if (x instanceof Promise) {

//? x.then(resolve, reject)

// } else {

//? // 為什么是resolve?

//? resolve(x)

// }

// 兼容

RESOLVE_PROMISE(promise2, x, resolve, reject)

} catch(e) {

reject(e)

}

})

})

}

if (self.status === 'pending') {

/*

* promise1還是不確定狀態(tài),需要等到promise1確地了狀態(tài)才能確定promise2應(yīng)該怎么走

* 查看構(gòu)造函數(shù)里resolve函數(shù)的注釋,找找靈感。

* -------------------------------------------------------

* 為什么在這就不需要像上面的‘resolved’,‘rejected’里異步呢?

*? ? 因?yàn)檫@里肯定是在將來(lái)才執(zhí)行的

*/

promise2 = new Promise(function(resolve, reject) {

self.resolvedCallbacks.push(function(value) {

try {

var x = onResolved(self.data)

// 非兼容

// if (x instanceof Promise) {

//? x.then(resolve, reject)

// } else {

//? resolve(x)

// }

// 兼容

RESOLVE_PROMISE(promise2, x, resolve, reject)

} catch(e) {

reject(e)

}

})

self.rejectedCallbacks.push(function(reason) {

try {

var x = onRejected(self.data)

// 非兼容

// if (x instanceof Promise) {

//? x.then(resolve, reject)

// } else {

//? resolve(x)

// }

// 兼容

RESOLVE_PROMISE(promise2, x, resolve, reject)

} catch(e) {

reject(e)

}

})

})

}

return promise2

}

/*

* 上面就基本已經(jīng)實(shí)現(xiàn)了Promise,但是沒(méi)有達(dá)到 https://promisesaplus.com/ 規(guī)范的要求。

* 這個(gè)函數(shù)是多種實(shí)現(xiàn)中的一種

* 這個(gè)函數(shù)的作用是使其他的Promise可以互相兼容------兼容

* ------------------------------------------------------------------------

* 各參數(shù)說(shuō)明

*? ? promise:

*? ? x:

*? ? resolve:

*? ? reject:

*/

function RESOLVE_PROMISE(promise, x, resolve, reject) {

if (promise === x) {

// 一個(gè)promise實(shí)例resolve了它自己

reject(new TypeError('Chaining cycle is deteced for promise'))

return

}

// 如果x是自己的Promise的實(shí)現(xiàn)

if (x instanceof Promise) {

if (x.status === 'pending') {

x.then(function(v) {

// god,又是遞歸

RESOLVE_PROMISE(promise, v, resolve, reject)

}, reject)

} else {

x.then(resolve, reject)

}

return

}

// 只有對(duì)象和函數(shù)這兩種數(shù)據(jù)類型可以保存屬性,然而typeof null也是'object'

if (x !== null && (typeof x === 'object' || typeof x === 'function')) {

try {

/*

* 為什么要把then單獨(dú)拿出來(lái)?避免副作用

*? 因?yàn)槲覀兪窃谧x別人給的屬性,但是我們不知道該屬性具體是什么。也可能報(bào)錯(cuò)

*? 如果該屬性是getter,則每次調(diào)用就會(huì)返回不同的值。

* 所以只調(diào)用一次

* ---------------------------------------------------------------------

* a,b,c三者只能調(diào)用一個(gè),為什么?TODO

*? 避免原型鏈上有then函數(shù)(thenable)但卻不是Promise的實(shí)現(xiàn),但是為什么這樣就可以避免,我并沒(méi)有吃透

* ---------------------------------------------------------------------

* if語(yǔ)句里有兩個(gè)可以可疑的函數(shù),有函數(shù)名resolvePromise,rejectPromise。

* 既然是當(dāng)參數(shù)傳,我覺(jué)得不需要函數(shù)名應(yīng)該也一樣吧

*/

var then = x.then

var called = false // 記錄執(zhí)行狀態(tài)

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

then.call(x, function resolvePromise(y) {

if (called) {

return

}

called = true

// 產(chǎn)生了遞歸

RESOLVE_PROMISE(promise, y, resolve, reject)//--- a

}, function rejectPromise(r) {

if (called) {

return

}

called = true

reject(r) //------------------------------------- b

})

} else {

resolve(x)

}

} catch(e) {

if (called) {

return

}

called =true

reject(e) //----------------------------------------- c

}

} else {

resolve(x)

}

}

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

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

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