1. 程式人生 > 其它 >一個簡單的手寫Promise(持續完善中)

一個簡單的手寫Promise(持續完善中)

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        // 捕獲執行器錯誤
        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }
    // promise狀態
    status = PENDING
    // 成功之後的值
    value = undefined
    // 失敗之後的原因
    reason = undefined
    successCallback = []
    failCallback = []
    resolve = value => {
        if (this.status !== PENDING) return
        this.status = FULFILLED
        this.value = value
        // this.successCallback && this.successCallback(this.value)
        while (this.successCallback.length) this.successCallback.shift()()
    }
    reject = reason => {
        if (this.status !== PENDING) return
        this.status = REJECTED
        this.reason = reason
        // this.failCallback && this.failCallback(this.reason)
        while (this.failCallback.length) this.failCallback.shift()()
    }
    then(successCallback, failCallback) {
        // 若不傳回調函式,則直接將結果返回
        successCallback = successCallback ? successCallback : value => value
        failCallback = failCallback ? failCallback : value => { throw value }
        let promise = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    handlePromise(promise, successCallback, this.value, resolve, reject)
                }, 0)
            }
            else if (this.status === REJECTED) {
                setTimeout(() => {
                    handlePromise(promise, failCallback, this.reason, resolve, reject)
                }, 0)
            }
            else {
                this.successCallback.push(() => {
                    setTimeout(() => {
                        handlePromise(promise, successCallback, this.value, resolve, reject)
                    }, 0)
                })
                this.failCallback.push(() => {
                    setTimeout(() => {
                        handlePromise(promise, failCallback, this.reason, resolve, reject)
                    }, 0)
                })
            }
        })
        return promise
    }

    finally(callback) {
        return this.then(res => {
            return MyPromise.resolve(callback()).then(() => res)
        }, err => {
            throw MyPromise.resolve(callback()).then(() => { throw err })
        })
    }

    catch(failCallback) {
        return this.then(undefined, failCallback)
    }

    static all(array) {
        let result = []
        let index = 0
        return new MyPromise((resolve, reject) => {
            function addResult(key, value) {
                result[key] = value
                if (++index === array.length) resolve(result)
            }
            for (let i = 0; i < array.length; i++) {
                const element = array[i]
                if (element instanceof MyPromise) {
                    // element 是promise物件
                    element.then(v => addResult(i, v), e => reject(e))
                } else {
                    addResult(i, element)
                }
            }
        })
    }

    static resolve(value) {
        if (value instanceof MyPromise) {
            return value
        } else {
            return new MyPromise((resolve, reject) => {
                resolve(value)
            })
        }
    }

    static race(array) {
        return new MyPromise((resolve, reject) => {
            function addResult(key, value) {
                result[key] = value
                if (++index === array.length) resolve(result)
            }
            for (let i = 0; i < array.length; i++) {
                const element = array[i]
                if (element instanceof MyPromise) {
                    // element 是promise物件
                    element.then(v => resolve(v), e => reject(e))
                } else {
                    resolve(element)
                }
            }
        })
    }
    static allSettled(array) {
        let result = []
        let index = 0
        return new MyPromise((resolve, reject) => {
            function addResult(key, value) {
                result[key] = value
                if (++index === array.length) resolve(result)
            }
            for (let i = 0; i < array.length; i++) {
                const element = array[i]
                if (element instanceof MyPromise) {
                    // element 是promise物件
                    element.then(v => addResult(i, { status: 'fulfilled', value: v }), e => addResult(i, { status: 'rejected', reason: e }))
                } else {
                    addResult(i, { status: 'fulfilled', value: element })
                }
            }
        })
    }
}

function handlePromise(promise, callback, value, resolve, reject) {
    try {
        let x = callback(value)
        if (promise === x) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        if (x instanceof MyPromise) {
            x.then(resolve, reject)
        } else {
            resolve(x)
        }
    } catch (error) {
        reject(error)
    }
}
module.exports = MyPromise