首先我們得先理解Promise工作的機制,其實Promise就是一個構造函數
它的內部有三個狀態:
- pending
- resolved
- rejected
而想改變這三個狀態必須要通過resolve()或者reject()這兩個方法,resolve()可以將pending轉換為resolved,rejected()可以將pending轉換為rejected。并且將得到的數值存儲在內部的data里面。并且這狀態一旦轉換是不可逆的。
Promsie的原型對象含有then,catch這兩個方法
- then這個方法可以接受兩個參數,一個成功的回調,一個失敗的回調。也就是onResolved和onRejected
- catch這個方法只可以接受一個參數,失敗的回調,也就是onRejected
- 并且then這個方法,是返回一個新的promise對象,它里面的執行方法也是異步的
- 觸發then的時候,也會有三個可能,一個是狀態為resolved時,一個是狀態為rejected時,一個是狀態為pending時
- Promise的結果根據執行的結果返回
然后就可以開始自定義實現Promise了
一、大致框架列出來
- Promise函數會接受一個構造器,也就是接受一個函數,我們就命名為executor,executor會調用resolve和reject,所以我們就可以寫resolve和reject這兩個方法
- 定義pending,resolved,rejected。因為后面會多處用到,避免書寫錯誤,導致出現不必要的BUG
- 內部存儲狀態,和數據,初始狀態為pending,初始值為undefind
- Promise原型鏈上有then和catch兩個方法
- Promise函數對象有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函數對象的方法*/
Promise.resolve = function (value) {
}
Promise.reject = function (error) {
}
/*只有當所有promise成功時才成功*/
Promise.all = function (promises) {
}
/*其結果由第一個完成的promise決定*/
Promise.race = function (promises) {
}
executor(resolve,reject)
}
window.Promise = Promise
})()
二、寫函數內部resolve/reject方法
- 我們改變promise對象的狀態只能通過resolve/reject改變,所以當我們觸發方法,首先得改變狀態,然后存儲值
- 我們最開始已經定義了常量,所以直接用定義的值
- 因為resolve/reject改變狀態只能從pending開始,所以判斷如果狀態不是pending的時候,就不用執行下面代碼了。
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的結果根據執行的結果返回
- 當我們調用then的時候,會傳入兩個回調函數
- then方法返回的是一個新的promise對象
- 我們調用then時需要考慮三個結果,也就是當前狀態為pending時,為resolved時,為rejected時
- then里面所執行的方法是異步的
- 定義一個公共方法handle,減少代碼冗余性
- 當我們調用handle函數也要考慮三種情況,因為promise的結果是根據執行的結果返回
Promise.prototype.then = (onResolved,onRejected) => {
return new Promise((resolve,reject) => {
function handle(callback){
/*
1、如果拋出異常,return的promise就會失敗
2、如果回調函數返回的不是promise,return就會成功
3、如果回調函數返回的是promise,return的promise結果就是這個promise的結果
*/
try {
const result = callback(self.data)
if (result instanceof Promise) { //instanceof 運算符用來檢測 Promise.prototype 是否存在于參數 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)
}