手写Promise

47 阅读1分钟
const PENDING = 'pendind'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected';

// 模拟微队列
function runMicroTask(callback) {
    setTimeout(callback, 0)
}

function isPromise(obj) {
    return !!(obj && typeof obj === 'object' && typeof obj.then === 'function')
}


class MyPromise {
    constructor(exector) {
        this._value = undefined;
        this._state = PENDDING;
        this._handles = [];

        try {
            exector(this._resolve.bind(this), this._reject.bind(this))
        } catch (error) {
            this._reject(error)
        }
    }

    _changeState(state, value) {
        if (this._state !== PENDDING) {
            return
        }
        this._state = state;
        this._value = value;
        this._runHandles()
    }

    _resolve(data) {
        this._changeState(FULFILLED, data)
    }

    _reject(reason) {
        this._changeState(REJECTED, reason)
    }

    then(onFulfilled, onRejected) {
        return new MyPromise((resolve, reject) => {
            this._handles.push({ onFulfilled, FULFILLED, resolve, reject })
            this._handles.push({ onRejected, REJECTED, resolve, reject })
            this._runHandles()
        })
    }

    _runOneHandles({ exector, state, resolve, reject }) {
        runMicroTask(() => {
            if (this._state !== state) {
                return
            }

            if (typeof exector !== 'function') {
                this._state === FULFILLED ? resolve(this._value) : reject(this._value)
                return
            }

            try {
                const result = exector(this._value)
                if (isPromise(result)) {
                    result.then(resolve, reject)
                } else {
                    resolve(result)
                }
            } catch (error) {
                reject(error)
            }
        })
    }

    _runHandles() {
        if (this._state === PENDDING) {
            return
        }

        while (this._handles[0]) {
            this._runOneHandles(this._handles[0])
            this._handles.shift()
        }
    }

    static resolve(data) {
        if (data instanceof MyPromise) {
            return data
        }

        return new MyPromise((resolve, reject) => {
            if (isPromise(data)) {
                data.then(resolve, reject)
            } else {
                resolve(data)
            }
        })
    }

    static catch(onRejected) {
        return this.then(null, onRejected)
    }

    /**
     * Promise.race()特点
     * @param {*} proms 参数为一个数组,且数组元素为promise类型数据
     * @returns 返回值为一个promise,且返回值为第一个成功或者失败的promise的值
     */
    static race(proms) {
        return new MyPromise((resolve, reject) => {
            for (const p of proms) {
                MyPromise.resolve(p).then(resolve, reject)
            }
        })
    }


    static all(proms) {
        return new MyPromise((resolve, reject) => {
            try {
                const result = [];
                const count = 0;
                const fulfilleCount = 0;
                for (const p of proms) {
                    let index = count;
                    count++;
                    MyPromise.resolve(p).then((data) => {
                        fulfilleCount++;
                        result[index] = data
                        if (fulfilleCount === count) {
                            resolve(result)
                        }
                    }, reject)
                }
                if (count === 0) {
                    resolve(result)
                }
            } catch (error) {
                reject(error)
            }
        })
    }

    static AllSettled(proms) {
        const ps = [];
        for (const p of proms) {
            ps.push(MyPromise.resolve(p).then(((value) => ({state: FULFILLED, value})), (reason) => ({state: REJECTED, reason})))
        }
        return MyPromise.all(ps)
    }
    
    static finally(onSettled){
        return this.then((data) => {
            onSettled()
            return data
        }, (reason) => {
            onSettled()
            throw reason
        })
     }
}