手寫一個符合promise/A+規範的promise庫

promise使用及原理分析:

  • 經過new關鍵字建立promise實例, 接受一個executor參數, executor方法返回兩個方法 resolve, reject, 可用經過在executor方法中經過調用resolve(使成功)或調用reject(使失敗),來控制promise狀態
let p = new Promise((resolve, reject) => {
    resolve(100)
})
  • executor中能夠執行同步代碼也能夠執行異步代碼
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(100)
    })
})
  • promise原型對象上有then方法供promise實例調用, then方法接受兩個參數onFulfilled, onRejected
  • onFulfilled默認爲一個函數 data => data
  • onRejected默認爲一個函數 err => {throw err}
  • 當promise狀態爲fulfilled時, 執行用戶傳入的onFulfilled方法
  • 當promise狀態爲rejected時, 執行用戶傳入的onRected方法
  • 當用戶建立promise對象時採用異步,那麼執行then方法時沒有調用resolve方法或reject方法,因此promise狀態依然爲pending狀態,因此須要在then方法中採起發佈訂閱模式,先保存then方法傳來的onFulfilled和onReje
  • 又由於同一個promise實例能夠調用屢次then方法,從而傳多個onFulfilled和onRected,因此發佈訂閱採用數組保存
  • onFulfilled和onRejected方法中可能throw Error, 因此在執行onFulfilled和onRejected時須要try catch捕獲
  • then方法返回一個新的Promise實現鏈式編程
  • 統一判斷then方法傳入的onFulfilled方法和onRejected方法中return的類型(resolvePromise)
p.then(data => {
    console.log(data)
}, err => {
    console.log(err)
})
let p1 = p.then(data => {
    return p1   // 報類型錯誤
})
p.then(data => {
    return new Promise((resolve, reject) => {
        resolve(100)
    })
})
  • catch方法接受一個錯誤回調,能夠用then方法實現(語法糖)
  • ES2018 中新增finally方法 也能夠經過then方法實現(語法糖) finally要實現值得穿透, finally前若是有then方法,其返回值要穿過finally方法傳給以後的then
p.then(data => {
    return 100
}).finally(() => {
    console.log('finally')
}).then(data => {
    console.log(data)  // 100
})
  • all, race 方法都接受一個promise數組
  • all方法要全部promise都返回才resolve一個所有是成功態的數組,只要有一個rejected就直接reject
  • race方法只要有一個promise resolve就直接resolve

完整代碼以下:

class Promise {
    constructor(executor) {
        this.status = Promise.PENDING
        this.value = undefined
        this.reason = undefined
        // 發佈訂閱的存儲器onResolvedCallbacks, onRejectedCallbacks
        this.onResolvedCallbacks = []
        this.onRejectedCallbacks = []
        this.initBind()
        this.init(executor)
    }
    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    init(executor) {
        // 防止executor中拋錯
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    resolve(data) {
        // 若是resolve中傳入一個promise, 那麼返回改promise結果
        if (data instanceof Promise) data.then(this.resolve, this.reject)
        if (this.status === Promise.PENDING) {
            this.status = Promise.FULFILLED
            this.value = data
            this.onResolvedCallbacks.forEach(fn => fn())
        }
    }
    reject(reason) {
        if (this.status === Promise.PENDING) {
            this.status = Promise.REJECTED
            this.reason = reason
            this.onRejectedCallbacks.forEach(fn => fn())
        }
    }
    then(onFulfilled, onRejected) {
        const fulfilledHandle = (resolve, reject) => {
            // 此處用setTimeout異步才能拿到promise2
            setTimeout(() => {
                try {
                    let x = onFulfilled(this.value)
                    Promise.resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            })
        }
        const rejectHandle = (resolve, reject) => {
            setTimeout(() => {
                try {
                    let x = onRejected(this.reason)
                    Promise.resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            })
        }
        // onFulfilled和onRejected定義默認值
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected = typeof onRejected === 'function' ? onRejected : reason => {
            throw reason
        }
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === Promise.FULFILLED) {
                fulfilledHandle(resolve, reject)
            }
            if (this.status === Promise.REJECTED) {
                rejectHandle(resolve, reject)
            }
            if (this.status === Promise.PENDING) {
                this.onResolvedCallbacks.push(() => {
                    fulfilledHandle(resolve, reject)
                })
                this.onRejectedCallbacks.push(() => {
                    rejectHandle(resolve, reject)
                })
            }
        })
        // 返回一個新的promise
        return promise2
    }
    catch (onRejected) {
        return this.then(null, onRejected)
    }
    static resolve() {
        return new Promise((resolve, reject) => {
            resolve()
        })
    }
    static reject() {
        return new Promise((resolve, reject) => {
            reject()
        })
    }
    finally(callback) {
        return this.then(
            data => Promise.resolve(callback()).then(() => data),
            err => Promise.resolve(callback()).then(() => {
                throw err
            })
        )
    }
    static all(promises) {
        return new Promise((resolve, reject) => {
            let result = []
            let count = 0
            const setResult = (key, value) => {
                result[key] = value
                if (++count === promises.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < promises.length; i++) {
                let current = promises[i]
                if (Promise.isPromise(current)) {
                    current.then(data => {
                        setResult(i, data)
                    }, reject)
                } else {
                    setResult(i, current)
                }
            }
        })
    }
    static race(promises) {
        return new Promise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                let current = promises[i]
                if (Promise.isPromise(current)) {
                    current.then(resolve, reject)
                } else {
                    resolve(current)
                }
            }
        })
    }
}
Promise.PENDING = 'pending'
Promise.FULFILLED = 'fulfilled'
Promise.REJECTED = 'rejected'
Promise.resolvePromise = (promise2, x, resolve, reject) => {
    // called防止他人的promise即執行resolve又執行 reject
    let called
    if (promise2 === x) throw new TypeError('xxx')
    if (typeof x === 'function' || typeof x === 'object' && x !== null) {
        try {
            let then = x.then
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return
                    called = true
                    // 遞歸解析,總有一個結果then方法返回一個普通值
                    Promise.resolvePromise(promise2, y, resolve, reject)
                }, e => {
                    if (called) return
                    called = true
                    reject(e)
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true
            reject(e)
        }
    } else {
        resolve(x)
    }
}
Promise.isPromise = (obj) => {
    return typeof obj === 'function' || typeof obj === 'object' && obj !== null && obj.then && typeof obj.then === 'function'
}
// 延遲對象
Promise.deferred = () => {
    const defer = {}
    defer.promise = new Promise((resolve, reject) => {
        defer.resolve = resolve
        defer.reject = reject
    })
    return defer
}

module.exports = Promise
相關文章
相關標籤/搜索