認真看完這篇文章, 您能夠本身封裝一個簡易但功能相對齊全的Promise, 還能夠加深對Promise的理解es6
建議 : 看這篇文章以前但願您數組
文章較長, 代碼連貫性較強, 從簡單開始入手, 讀者能夠按需選讀promise
class Promise {
constructor (executor) {
if (typeof executor !== 'function')
throw new TypeError(`Promise resolver ${executor} is not a function`)
/* 默認狀態 */
this.state = 'pending'
this.value = undefined
this.reason = undefined
/*
狀態函數 resolve, reject
1.pending -> fulfilled, pending -> rejected
2.把數據儲存到Promise實例上 this.value = value, this.reason = reason
*/
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
}
}
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
}
}
executor(resolve, reject)
}
then (onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
if (this.state === 'rejected') {
onRejected(this.reason)
}
}
}
複製代碼
ps : 測試工具爲vsCode的Quokka插件bash
根據Promise
的狀態函數res
和rej
,對應執行then
中的處理函數onFulfilled
和onRejected
異步
咱們都知道,Promise中的then函數的代碼是異步執行的,而咱們寫的這個並非,能夠驗證一下函數
顯然這段代碼是同步執行的,而咱們想要的輸出順序是 0 2 1
,因此咱們可使用setTimeout模擬這個異步工具
class Promise {
constructor (executor) { ... }
then (onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
/* 使用setTimeout模擬異步 */
setTimeout(() => {
onFulfilled(this.value)
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
onRejected(this.reason)
}, 0);
}
}
}
複製代碼
ok, 完美獲得咱們想要的!
res/rej
爲異步執行時, 咱們能夠看到
then
是沒有反應的
左邊灰色小方塊代表這行代碼沒有執行
爲何呢? 那是由於當執行到then
函數的時候,res
爲異步執行,因此狀態仍是pending
,而咱們的then
函數裏面尚未對狀態爲pending
的處理, 修改一下代碼測試
class Promise {
constructor (executor) {
...
/* 狀態函數異步執行時, 處理函數的存儲列表 */
this.resolveCallBackList = []
this.rejectCallBackList = []
const resolve = value => {
if (this.state === 'pending') {
...
/* 若是有, 則執行處理函數列表裏的函數 */
this.resolveCallBackList.length > 0
&& this.resolveCallBackList.forEach(e => e())
}
}
const reject = reason => {
if (this.state === 'pending') {
...
this.rejectCallBackList.length > 0
&& this.rejectCallBackList.forEach(e => e())
}
}
...
}
then (onFulfilled, onRejected) {
...
/* 狀態爲pending時, 把處理函數存儲對相應的列表 */
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
onFulfilled(this.value)
})
onRejected && this.rejectCallBackList.push( () => {
onRejected(this.reason)
})
}
}
}
複製代碼
這樣, 狀態函數異步執行的時候也能夠處理了, 能夠簡單理解爲, 當狀態爲pending
時, 把處理函數onFulfilled/onRejected
存起來, 等狀態函數res/rej
執行時, 自動執行對應的處理函數ui
當發生錯誤時, Promise
不會報錯, 而是由失敗的處理函數then函數的第二個函數
捕捉錯誤並處理, 若是咱們本身寫的Promise
發生錯誤的話, 毫無心外是直接報錯的, 就像這樣this
既然執行時發生錯誤, 那麼咱們就可使用try/catch
去捕獲錯誤
class Promise {
constructor (executor) {
...
/* 使用try/catch捕獲錯誤, 並執行reject, 改變狀態爲rejected */
try {
executor(resolve, reject)
} catch (error) {
this.state === 'pending' && reject(error)
}
}
then (onFulfilled, onRejected) { ... }
}
複製代碼
then函數有兩個特性
new Promise(res => res(0))
.then(value => {
console.log(value) // 0
return `1 fulfilled`
})
.then(value => {
console.log(value) // 1 fulfilled
})
複製代碼
then
函數執行後返回一個Promise
實例, 該Promise
實例的狀態由then
決定, 下一個then
函數根據返回的這個Promise
實例執行相應的處理函數, 畫個圖
then
的執行依賴於上一個
then
執行返回的
Promise
實例, 而這個
Promise
實例的數據由上一個
then
的處理函數
onFulfilled/onRejected
的
執行和其返回值
決定
若是按照字面意思去寫代碼
class Promise {
constructor (executor) { ... }
then (onFulfilled, onRejected) {
/* 一個新的Promise實例 */
const newPromise = new Promise ( (res, rej) => {})
...
return newPromise
}
}
複製代碼
若是這樣寫, 是沒意義的, 返回的Promise
實例的狀態永遠爲pending
, 由於沒有執行狀態函數res/rej
, 所以也沒法進行then
函數的鏈式調用
由於new Promise(executor)
的executor
函數是同步執行的, 因此咱們能夠這樣寫
class Promise {
constructor (executor) { ... }
then (onFulfilled, onRejected) {
const newPromise = new Promise ( (res, rej) => {
/*
這部分的處理函數是同步執行的, 所以能夠放在裏面執行
同時還能經過res/rej改變返回的Promise實例的狀態
*/
if (this.state === 'fulfilled') {
setTimeout(() => {
/* 拿處處理函數執行後的返回值 */
const value = onFulfilled(this.value)
/* 改變返回的Promise實例的狀態並把數據傳過去 */
res(value)
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
const reason = onRejected(this.reason)
res(reason)
}, 0);
}
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
const value = onFulfilled(this.value)
res(value)
})
onRejected && this.rejectCallBackList.push( () => {
const reason = onRejected(this.reason)
res(reason)
})
}
})
return newPromise
}
}
複製代碼
噠噠,
then
的鏈式調用完成了
ps : then
的處理函數返回值不是一個Promise
實例時, 不管fullfilled
仍是rejected
, 都是執行下一個then
函數的onFulfilled
當then
的處理函數返回值是一個Promise
實例時, 則下一個then
函數的執行, 所有由這個Promise
實例決定, 因此咱們須要使用checkReturnValueIfPromise
函數去判斷一下返回值的類型並處理對應的狀況
class Promise {
constructor (executor) { ... }
/*
promise -> Promise對象
target -> then的處理函數的返回值
res/rej -> 要返回的Promise實例的狀態函數
*/
checkReturnValueIfPromise (promise, target, res, rej) {
if (target instanceof promise) {
/*
若是是Promise實例
則調用then函數,根據Promise實例的狀態執行對應的處理函數
從而改變要返回的Promise實例的狀態
若是下面的代碼不能理解, 也能夠寫成這樣
target.then( value => {
res(value)
}, reason => {
rej(reason)
} )
*/
target.then(res, rej)
} else {
res(target)
}
}
then (onFulfilled, onRejected) {
const newPromise = new Promise ( (res, rej) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
const value = onFulfilled(this.value)
/* 調用檢測函數並作相關處理 */
this.checkReturnValueIfPromise(Promise, value, res, rej)
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
const reason = onRejected(this.reason)
this.checkReturnValueIfPromise(Promise, reason, res, rej)
}, 0);
}
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
const value = onFulfilled(this.value)
this.checkReturnValueIfPromise(Promise, value, res, rej)
})
onRejected && this.rejectCallBackList.push( () => {
const reason = onRejected(this.reason)
this.checkReturnValueIfPromise(Promise, reason, res, rej)
})
}
})
return newPromise
}
}
複製代碼
就算是異步也是一點毛病都沒有
對了, 還有一個與then
相似的方法catch
, 這個方法是專門處理rejected
狀態的, 代碼也就只有一句話
class Promise {
constructor () { ... }
then () { ... }
catch (onRejected) {
this.then(undefined, onRejected)
}
}
複製代碼
返回一個fulfilled
狀態的Promise
實例
class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve (value) {
return new Promise( res => res(value))
}
}
複製代碼
返回一個rejected
狀態的Promise
實例
class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve () { ... }
static reject (reason) {
return new Promise( (undefined, rej) => rej(reason))
}
}
複製代碼
接收一個Promise
實例的數組promiseArray
, 返回一個Promise
實例, 返回的Promise
實例由promiseArray
中執行最快的Promise
實例決定
class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve () { ... }
static reject () { ... }
static race (promiseArray) {
return new Promise ( (res, rej) => {
promiseArray.forEach( promise => {
promise.then(res, rej)
})
})
}
}
複製代碼
功能描述太長了, 不懂的能夠去看 阮一峯老師對於Promise.all的介紹
class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve () { ... }
static reject () { ... }
static race () { ... }
static all (promiseArray) {
let count = 0,
resultArray = []
return new Promise( (res, rej) => {
promiseArray.forEach( promise => {
promise.then( value => {
count++
resultArray.push(value)
if (count === promiseArray.length) {
res(resultArray)
}
}, reason => {
rej(reason)
})
})
})
}
}
複製代碼
謝謝瀏覽個人文章, 但願你能學到東西