模擬實現Promise,探究Promise原理
promise的功能
建構函式的實現
首先寫一段promise的程式碼來分析一下建構函式的實現:
let pro = new Promise((resolve, reject) => {
if (true) {
resolve('the value')
} else {
reject('the reason')
}
})
promise一共有pending(進行中)、fulfilled(已成功)、rejected(已失敗)三種狀態,一旦狀態確定就不可更改。初始狀態為pending,而且狀態變更只有兩種:
pending --> fulfilled
pending --> rejected
promise的建構函式,傳了一個函式作為引數,這個函式接受promise返回的兩個函式:resolve、reject,在建構函式中呼叫,呼叫後改變狀態。
便可以寫出如下建構函式:
const PENDING = 'pending' // 進行中
const FULFILLED = 'fulfilled' // 已成功
const REJECTED = 'rejected' // 已失敗
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
// 初始狀態PENDING
status = PENDING
resolve() {
// 只有PENDING狀態能變成FULFILLED狀態
if (this.status !== PENDING) return
// 狀態改變為成功
this.status = FULFILLED
}
reject() {
// 只有PENDING狀態能變成REJECTED狀態
if (this.status !== PENDING)
// 狀態變為失敗
this.status = REJECTED
}
}
then方法的實現
呼叫一下上述寫的promise
pro.then(res => {
console.log(res)
}, (reason) => {
console.log(reason)
})
promise原型上有一個then方法。then方法接受成功回撥和失敗回撥兩個引數。then方法內部判斷狀態, 如果是成功就呼叫成功回撥函式,如果是失敗就呼叫失敗回撥函式。
建構函式如果呼叫resolve會傳遞一個值,作為then方法中成功回撥的引數;如果呼叫reject會傳遞一個值,作為then方法失敗回撥的引數。
便可以寫出如下程式碼:
const PENDING = 'pending' // 進行中
const FULFILLED = 'fulfilled' // 已成功
const REJECTED = 'rejected' // 已失敗
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
// 初始狀態PENDING
status = PENDING
// 成功之後的值
value = undefined
// 失敗之後的原因
reason = undefined
resolve = (value) => {
// 只有PENDING狀態能變成FULFILLED狀態
if (this.status !== PENDING) return
// 狀態改變為成功
this.status = FULFILLED
this.value = value
}
reject = (reason) => {
// 只有PENDING狀態能變成REJECTED狀態
if (this.status !== PENDING)
// 狀態變為失敗
this.status = REJECTED
this.reason = reason
}
then = (successCallback, failCallback) => {
if (this.status === FULFILLED) {
successCallback(this.value)
} else {
failCallback(this.reason)
}
}
}
tips: 每一步可以自行呼叫,驗證是否已實現相關功能。
完善then支援非同步
很明顯我們的方法是不支援非同步的,接下來實現非同步功能:
let pro = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 1000)
})
pro.then((res) => {
console.log(res)
})
很明顯,我們需要在上述setTimeout內部執行的時候呼叫resolve,去改變狀態。那then方法就會先呼叫,所以這時候我們應該在then的時候增加一種pending狀態判斷,這時候要把then傳入的函式快取起來。等到resolve呼叫的時候再去執行。實現如下:
// 快取成功回撥函式
successCallback = null
// 快取失敗回撥函式
failCallback = null
resolve = (value) => {
// 只有PENDING狀態能變成FULFILLED狀態
if (this.status !== PENDING) return
// 狀態改變為成功
this.status = FULFILLED
this.value = value
// 執行成功回撥
if (this.successCallback) this.successCallback(this.value)
}
reject = (reason) => {
// 只有PENDING狀態能變成REJECTED狀態
if (this.status !== PENDING)
// 狀態變為失敗
this.status = REJECTED
this.reason = reason
// 執行失敗回撥
if (this.failCallback) this.failCallback(this.reason)
}
then = (successCallback, failCallback) => {
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
/**
* pending狀態 暫存成功回撥和失敗回撥
* 非同步情況處理
* **/
this.successCallback = successCallback
this.failCallback = failCallback
}
}
鏈式呼叫
promise是支援鏈式呼叫的,並且上一步的返回值會作為下一步的引數傳入:
pro.then((res) => {
console.log(res) // 100
return 1000
}).then(res => {
console.log(res) // 1000
return 10000
}).then(res => {
console.log(res) // 10000
})
//
pro.then().then((res) => console.log(res)) // 100
要實現鏈式呼叫,那then呼叫的時候還是要返回promise物件,因為then方法在promise上, 暫且將返回的promise物件叫做promise2,then方法就叫做then2。
let promise2 = new MyPromise((resolve, reject) => {})
這個返回的promise2呼叫resolve、reject的時機應該根據then2傳入的successCallback、failCallback來決定。
這裡要分為成功、失敗和非同步兩種情況
成功、失敗狀態:
判斷result的值是普通值還是promise物件:
如果是普通值 直接呼叫resolve
如果是promise物件 檢視promise物件返回的結果,再根據promise物件返回的結果 決定呼叫resolve還是呼叫reject
非同步:
暫存成功回撥和失敗回撥
這時候我們要把多次呼叫的回撥函式快取起來,快取到陣列,resolve呼叫時,再逐一呼叫快取的回撥
當鏈式呼叫沒有傳遞任何引數時,要向下傳遞值,所以我們在then內部,開始的時候判斷, 如果沒有成功或者失敗回撥,要直接返回值、或者失敗原因:
/**
* then不傳遞引數的時候不引數
* 逐級傳遞引數
***/
successCallback = successCallback ? successCallback:value => value
failCallback = failCallback ? failCallback:reason => { throw reason }
具體實現如下:
// 快取成功回撥函式
successCallback = []
// 快取失敗回撥函式
failCallback = []
resolve = (value) => {
// 只有PENDING狀態能變成FULFILLED狀態
if (this.status !== PENDING) return
// 狀態改變為成功
this.status = FULFILLED
this.value = value
if (this.successCallback.length) this.successCallback.shift()(this.value)
}
reject = (reason) => {
// 只有PENDING狀態能變成REJECTED狀態
if (this.status !== PENDING)
// 狀態變為失敗
this.status = REJECTED
this.reason = reason
if (this.failCallback.length) this.failCallback.shift()(this.reason)
}
then = (successCallback, failCallback) => {
/**
* then不傳遞引數的時候不引數
* 逐級傳遞引數
* **/
successCallback = successCallback ? successCallback:value => value
failCallback = failCallback ? failCallback:reason => { throw reason }
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
/**
* 判斷result的值是普通值還是promise物件:
* 如果是普通值 直接呼叫resolve
* 如果是promise物件 檢視promise物件返回的結果
* 再根據promise物件返回的結果 決定呼叫resolve還是呼叫reject
* **/
let result = successCallback(this.value)
checkPromise(result, resolve, reject)
} else if (this.status === REJECTED) {
let result = failCallback(this.reason)
checkPromise(result, resolve, reject)
} else {
/**
* pending狀態 暫存成功回撥和失敗回撥
* 非同步情況處理
* **/
this.successCallback.push(() => {
/**
* 判斷result的值是普通值還是promise物件:
* 如果是普通值 直接呼叫resolve
* 如果是promise物件 檢視promise物件返回的結果
* 再根據promise物件返回的結果 決定呼叫resolve還是呼叫reject
* **/
let result = successCallback(this.value)
checkPromise(result, resolve, reject)
})
this.failCallback.push(() => {
/**
* 判斷result的值是普通值還是promise物件:
* 如果是普通值 直接呼叫resolve
* 如果是promise物件 檢視promise物件返回的結果
* 再根據promise物件返回的結果 決定呼叫resolve還是呼叫reject
* **/
let result = failCallback(this.value)
checkPromise(result, resolve, reject)
})
}
})
return promise2
}
到這裡 我們已經實現了promise的核心方法,下面再來補充promise 的一些呼叫方法。
catch實現
首先,為了程式碼的健壯性,我們要try-catch異常捕獲。
在建構函式的時候,當捕獲到異常要直接呼叫reject方法。
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
http://www.bijianshuo.com 軟文發稿平臺
在then方法中, 呼叫成功或失敗回撥的時候,如果捕獲到異常,就直接走reject方法了。
try {
let result = successCallback(this.value)
checkPromise(result, resolve, reject)
} catch(error) {
reject(error)
}
觀察一下:
pro.then()
.catch(e => {
})
catch 是promise原型上的,方法接收一個失敗回撥,實現如下:
catch = (failCallback) => {
this.then(null, failCallback)
}