手撕Promise代碼

我的思路是根據這個Promise的使用方法,一步步用js還原其原理

// 這個回調函數傳進來就開始執行了
let oP = new Promise((resolve, reject) => {
    resolve(1)
})

// 這個要根據你的狀態進行選擇 Fulfilled pending Rejected
oP.then((res) => {
    console.log(res)
}, (rej)=> {
    console.log(rej)
})
// 源碼實現
// Fulfilled pending Rejected
function MyPromise(executor) {
    let self = this
    this.status = 'pending'
    this.resolveValue = null
    this.rejectValue = null
    this.ResolveCallBackList = []
    this.RejectCallBackList = []

    function resolve(val) {
       if(self.status === 'pending') {
			self.status = 'Fulfilled'
        	self.resolveValue = val
       }
    }
    function reject(reason) {
       if(self.status === 'pending') {
			self.status = 'Rejected'
            self.rejectValue = reason      
       }
    }

    try {
        executor(resolve, reject)
    } catch(e) {
        reject(e)
    }
}

MyPromise.prototype.then = function(onFulfilled, onRejected) {
    let self = this
    if(this.status === 'Fulfilled') {
        onFulfilled(self.resolveValue)
    }
    if(this.status === 'Rejected') {
        onRejected(self.rejectValue)
    }
}

現在有了初步的實現,我們繼續完善功能,如果是出現如下情況

let oP = new MyPromise((resolve, reject) => {
	// resolve函數放在了異步事件中,那麼此時then函數會先於resolve執行
	// then函數無法拿到resolve函數所傳的值
	setTimeout(() => {
	      resolve(1)
	  });
})

解決方法:構造函數定義一個回調列表,分別對應
Fulfilled Rejected 這個狀態

// Fulfilled pending Rejected
function MyPromise(executor) {
    let self = this
    this.status = 'pending'
    this.resolveValue = null
    this.rejectValue = null
    this.ResolveCallBackList = []
    this.RejectCallBackList = []

    function resolve(val) {
        if (self.status === 'pending') {
            self.status = 'Fulfilled'
            self.resolveValue = val
            self.ResolveCallBackList.forEach(item => {
                item()
            })
        }
    }
    function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'Rejected'
            self.rejectValue = reason
            self.RejectCallBackList.forEach(item => {
                item()
            })
        }
    }

    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    let self = this
    if (this.status === 'Fulfilled') {
        onFulfilled(self.resolveValue)
    }
    if (this.status === 'Rejected') {
        onRejected(self.rejectValue)
    }
    if (this.status === 'pending') {
        self.ResolveCallBackList.push(function () {
            onFulfilled(self.resolveValue)
        })
        self.RejectCallBackList.push(function () {
            onRejected(self.rejectValue)
        })
    }
}

繼續添加功能:

功能1:由於then函數是一個微任務事件,這裏我們使用setTimeout進行模擬
功能2:實現then函數的一個鏈式調用
功能3:then函數中出異常,利用try、catch進行處理
功能四:如果是then函數傳入的參數出現空值,那麼需要對參數進行非空判斷並且進行重寫

// Fulfilled pending Rejected
function MyPromise(executor) {
    let self = this
    this.status = 'pending'
    this.resolveValue = null
    this.rejectValue = null
    this.ResolveCallBackList = []
    this.RejectCallBackList = []

    function resolve(val) {
        if (self.status === 'pending') {
            self.status = 'Fulfilled'
            self.resolveValue = val
            self.ResolveCallBackList.forEach(item => {
                item()
            })
        }
    }
    function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'Rejected'
            self.rejectValue = reason
            self.RejectCallBackList.forEach(item => {
                item()
            })
        }
    }

    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    if(!onFulfilled) {
        onFulfilled = function(val) {
            return val
        }
    }
    if(!onRejected) {
        onRejected = function(reason) {
            return reason
        }
    }
    let self = this
    let nextPromise = new MyPromise(function (res, rej) {
        if (self.status === 'Fulfilled') {
            setTimeout(() => {
                try {
                    let returnValue = onFulfilled(self.resolveValue)
                    res(returnValue)
                } catch (e) {
                    rej(e)
                }
            })
        }
        if (self.status === 'Rejected') {
            setTimeout(() => {
                try {
                    let returnValue = onRejected(self.rejectValue)
                    rej(returnValue)
                } catch (e) {
                    rej(e)
                }
            })
        }
        if (self.status === 'pending') {
            self.ResolveCallBackList.push(function () {
                setTimeout(() => {
                    try {
                        let returnValue = onFulfilled(self.resolveValue)
                        res(returnValue)
                    } catch (e) {
                        rej(e)
                    }
                })                
            })
           
            self.RejectCallBackList.push(function () {
                setTimeout(() => {
                    try {
                        let returnValue = onRejected(self.rejectValue)
                        rej(returnValue)
                    } catch (e) {
                        rej(e)
                    }
                })                
            })
        }
    })
    return nextPromise
}

最後一個功能:then函數中的參數(參數是個函數)返回的是一個MyPromise對象,我們應該如何處理。案例如下:

let oP = new Promise((resolve, reject) => {
  	resolve(1)
 })
 // 這裏有兩個promise之間的關係問題,實際上也是我們需要解決的問題
 oP.then((res) => {
     console.log(res)
     return new Promise((res, rej) => {
         res(1) // 這個返回來的Promise對象最後賦值給then函數的內部變量
     })
 }).then(res => {
     console.log('then: ', res)
 }) 

解決方法:

// Fulfilled pending Rejected
function MyPromise(executor) {
    let self = this
    this.status = 'pending'
    this.resolveValue = null
    this.rejectValue = null
    this.ResolveCallBackList = []
    this.RejectCallBackList = []

    function resolve(val) {
        if (self.status === 'pending') {
            self.status = 'Fulfilled'
            self.resolveValue = val
            self.ResolveCallBackList.forEach(item => {
                item()
            })
        }
    }
    function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'Rejected'
            self.rejectValue = reason
            self.RejectCallBackList.forEach(item => {
                item()
            })
        }
    }

    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

function ResolutionReturnPromise(returnValue, res, rej) {
    if(returnValue instanceof MyPromise) {
        returnValue.then((val) => {
            res(val)
        }, (reason) => {
            rej(reason)
        })
    } else {
        res(returnValue)
    }
}

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    if(!onFulfilled) {
        onFulfilled = function(val) {
            return val
        }
    }
    if(!onRejected) {
        onRejected = function(reason) {
            return reason
        }
    }
    let self = this
    let nextPromise = new MyPromise(function (res, rej) {
        if (self.status === 'Fulfilled') {
            setTimeout(() => {
                try {
                    let returnValue = onFulfilled(self.resolveValue)
                
                    ResolutionReturnPromise(returnValue, res, rej)
                } catch (e) {
                    rej(e)
                }
            })
        }
        if (self.status === 'Rejected') {
            setTimeout(() => {
                try {
                    let returnValue = onRejected(self.rejectValue)
                    ResolutionReturnPromise(returnValue, res, rej)
                } catch (e) {
                    rej(e)
                }
            })
        }
        if (self.status === 'pending') {
            self.ResolveCallBackList.push(function () {
                setTimeout(() => {
                    try {
                        let returnValue = onFulfilled(self.resolveValue)
                        ResolutionReturnPromise(returnValue, res, rej)
                    } catch (e) {
                        rej(e)
                    }
                })                
            })
           
            self.RejectCallBackList.push(function () {
                setTimeout(() => {
                    try {
                        let returnValue = onRejected(self.rejectValue)
                        ResolutionReturnPromise(returnValue, res, rej)
                    } catch (e) {
                        rej(e)
                    }
                })                
            })
        }
    })
    return nextPromise
}

這裏是個初稿,太困了,後續再附上詳細說明和修正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章