首先我們得先理解Promise工作的機制耍群,其實Promise就是一個構(gòu)造函數(shù)
它的內(nèi)部有三個狀態(tài):
- pending
- resolved
- rejected
而想改變這三個狀態(tài)必須要通過resolve()或者reject()這兩個方法,resolve()可以將pending轉(zhuǎn)換為resolved颊艳,rejected()可以將pending轉(zhuǎn)換為rejected投队。并且將得到的數(shù)值存儲在內(nèi)部的data里面祖能。并且這狀態(tài)一旦轉(zhuǎn)換是不可逆的。
Promsie的原型對象含有then蛾洛,catch這兩個方法
- then這個方法可以接受兩個參數(shù)养铸,一個成功的回調(diào),一個失敗的回調(diào)轧膘。也就是onResolved和onRejected
- catch這個方法只可以接受一個參數(shù)钞螟,失敗的回調(diào),也就是onRejected
- 并且then這個方法谎碍,是返回一個新的promise對象鳞滨,它里面的執(zhí)行方法也是異步的
- 觸發(fā)then的時候,也會有三個可能蟆淀,一個是狀態(tài)為resolved時拯啦,一個是狀態(tài)為rejected時澡匪,一個是狀態(tài)為pending時
- Promise的結(jié)果根據(jù)執(zhí)行的結(jié)果返回
然后就可以開始自定義實現(xiàn)Promise了
一、大致框架列出來
- Promise函數(shù)會接受一個構(gòu)造器褒链,也就是接受一個函數(shù)唁情,我們就命名為executor,executor會調(diào)用resolve和reject甫匹,所以我們就可以寫resolve和reject這兩個方法
- 定義pending,resolved,rejected甸鸟。因為后面會多處用到,避免書寫錯誤兵迅,導致出現(xiàn)不必要的BUG
- 內(nèi)部存儲狀態(tài)抢韭,和數(shù)據(jù),初始狀態(tài)為pending恍箭,初始值為undefind
- Promise原型鏈上有then和catch兩個方法
- Promise函數(shù)對象有resolve,reject,all,race方法
(function () {
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function Promise(executor){
this.status = PENDING
this.data = undefind
this.callbacks = []
resolve = () => {
}
reject = () => {
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
Promise.prototype.then = () => {
}
Promise.prototype.catch = () => {
}
/*Promise函數(shù)對象的方法*/
Promise.resolve = function (value) {
}
Promise.reject = function (error) {
}
/*只有當所有promise成功時才成功*/
Promise.all = function (promises) {
}
/*其結(jié)果由第一個完成的promise決定*/
Promise.race = function (promises) {
}
executor(resolve,reject)
}
window.Promise = Promise
})()
二刻恭、寫函數(shù)內(nèi)部resolve/reject方法
- 我們改變promise對象的狀態(tài)只能通過resolve/reject改變,所以當我們觸發(fā)方法扯夭,首先得改變狀態(tài)吠各,然后存儲值
- 我們最開始已經(jīng)定義了常量,所以直接用定義的值
- 因為resolve/reject改變狀態(tài)只能從pending開始勉抓,所以判斷如果狀態(tài)不是pending的時候,就不用執(zhí)行下面代碼了候学。
resolve = (value) => {
if(this.satus !== PENDING){
return
}
this.status = RESOLVED
this.data = value
if (this.callbacks.length > 0) {
setTimeout(() => {
this.callbacks.forEach(callbacksObj => {
callbacksObj.onResolved(value)
})
})
}
}
reject = (error) => {
if(this.satus !== PENDING){
return
}
this.status = REJECTED
this.data = error
if (this.callbacks.length > 0) {
setTimeout(() => {
this.callbacks.forEach(callbacksObj => {
callbacksObj.onRejected(error)
})
})
}
}
三藕筋、重頭戲開始,寫原型對象then方法梳码,Promise的結(jié)果根據(jù)執(zhí)行的結(jié)果返回
- 當我們調(diào)用then的時候隐圾,會傳入兩個回調(diào)函數(shù)
- then方法返回的是一個新的promise對象
- 我們調(diào)用then時需要考慮三個結(jié)果,也就是當前狀態(tài)為pending時掰茶,為resolved時暇藏,為rejected時
- then里面所執(zhí)行的方法是異步的
- 定義一個公共方法handle,減少代碼冗余性
- 當我們調(diào)用handle函數(shù)也要考慮三種情況濒蒋,因為promise的結(jié)果是根據(jù)執(zhí)行的結(jié)果返回
Promise.prototype.then = (onResolved,onRejected) => {
return new Promise((resolve,reject) => {
function handle(callback){
/*
1盐碱、如果拋出異常,return的promise就會失敗
2沪伙、如果回調(diào)函數(shù)返回的不是promise瓮顽,return就會成功
3、如果回調(diào)函數(shù)返回的是promise,return的promise結(jié)果就是這個promise的結(jié)果
*/
try {
const result = callback(self.data)
if (result instanceof Promise) { //instanceof 運算符用來檢測 Promise.prototype 是否存在于參數(shù) result 的原型鏈
result.then(
value => {resolve(value)},
error => {reject(error)},
)
//result.then(resolve, reject)
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
if(this.status === PENDING){
this.callbacks.push({
onResolved: () => {
handle(onResolved)
},
onRejected: () => {
handle(onRejected)
}
})
} else if(this.status === RESOLVED){
setTimeout(() => {
handle(onRejected)
})
} else if(this.status === REJECTED){
setTimeout(() => {
handle(onResolved)
})
}
})
}
四围橡、寫原型對象catch方法
Promise.prototype.catch = function (onRejected) {
return this.then(undefined,onRejected)
}