手写ts-promise 自制

77 阅读1分钟
  • ts-Promise
enum enmuStatus {
    PENDING = 'pending',
    FULFILLED = 'fulfilled',
    REJECTED = 'rejected'
}
type DataType = (data?: ResolveAndReject['resolve']) => any | undefined
type ErrorType = (error?: ResolveAndReject['resolve']) => any
type PromiseStatus = 'fulfilled' | 'rejected' | 'pending'
interface HandlersObj {
    funState: PromiseStatus
    resolve: ResolveAndReject['resolve']
    reject: ResolveAndReject['reject']
    callback: DataType | undefined
}
interface ResolveAndReject {
    resolve(something: any): void
    reject(something: any): void
}
interface PromiseInterface {
    then(successFunc?: DataType, errorFunc?: ErrorType): void
}
function runMicroTask(callback: any) {
    if (globalThis.MutationObserver) {
        const p = document.createElement('p');
        const observer = new MutationObserver(callback);
        observer.observe(p, {
            childList: true, // 观察该元素内部的变化
        });
        p.innerHTML = '1';
    } else {
        setTimeout(callback, 0);
    }
}
export default class MyPromise implements PromiseInterface {
    private value: any
    private state: PromiseStatus
    private handlers: Array<HandlersObj>
    constructor(func: (resolve: ResolveAndReject["resolve"], reject: ResolveAndReject['reject']) => void) {
        try {
            func(this.resolve.bind(this), this.reject.bind(this))
        } catch (error) {
            this.reject(error)
            console.error(error)
        }
        this.value = null;
        this.state = enmuStatus.PENDING;
        this.handlers = []
    }
    private resolve(something: any): void {
        this.changeState(enmuStatus.FULFILLED, something)
    }
    private reject(something: any): void {
        this.changeState(enmuStatus.REJECTED, something)
    }
    private changeState(state: PromiseStatus, value: any) {
        if (this.state !== enmuStatus.PENDING) {
            return;
        }
        this.state = state;
        this.value = value;
        this.runHandler()
    }

    private runHandler() {
        if (this.state === enmuStatus.PENDING) {
            return;
        }
        while (this.handlers[0]) {
            this.runOneHandler(this.handlers[0])
            this.handlers.shift()
        }
    }
    private runOneHandler({ funState, resolve, reject, callback }: HandlersObj) {
        runMicroTask(() => {
            if (this.state !== funState) {
                return;
            }
            if (typeof callback !== 'function') {
                this.state === enmuStatus.FULFILLED ? resolve(this.value) : reject(this.value)
                return;
            }
            try {
                const result = (<DataType>callback)(this.value)
                if (result instanceof MyPromise) {
                    <PromiseInterface>result.then(resolve, reject)
                    return;
                }
                resolve(result)
            } catch (error) {
                reject(error)
                console.error(error)
            }
        })
    }
    private setHandle(funState: HandlersObj['funState'], resolve: HandlersObj['resolve'], reject: HandlersObj['reject'], callback: HandlersObj['callback']) {
        this.handlers.push(
            {
                funState,
                resolve,
                reject,
                callback
            }
        )
    }
    then(successFunc?: DataType, errorFunc?: ErrorType): PromiseInterface {
        return new MyPromise((resolve, reject) => {
            this.setHandle(enmuStatus.FULFILLED, resolve, reject, successFunc)
            this.setHandle(enmuStatus.REJECTED, resolve, reject, errorFunc)
            this.runHandler()
        })
    }
}