PromiseA+规范实现,通过Promise官方872个测试用例,纯代码

1,167 阅读2分钟

Promise A+ 规范实现完整版

本文正在参加「金石计划」

原文出自:圆圆01

Promsie实现详细解析1

Promise实现详细解析2

之前 promise 手写过很多次,也没有写过一篇完整的,今天学习大佬跟着写一篇完整的promise

先看成果,能通过所有的测试用例

image.png
type AllSettledType = {
        status: "fulfilled" | "rejected",
        value: any
}
// myPromise A+
class myPromise {

        private PENDING = "pending"
        private FULFILLED = "fullfilled"
        private REJECTED = "rejected"
        private promiseState: string
        private promiseResult: any
        private onFulfilledCallbacks: Function[]
        private onRejectedCallbacks: Function[]



        constructor(executor: (arg0: (res: any) => void, arg1: (err: any) => void) => void) {
                this.promiseState = this.PENDING
                this.promiseResult = null
                this.onFulfilledCallbacks = []
                this.onRejectedCallbacks = []

                const _resolve = (result: any) => {

                        if (this.promiseState === this.PENDING) {
                                this.promiseState = this.FULFILLED
                                this.promiseResult = result
                                this.onFulfilledCallbacks.forEach(callback => callback(result))
                        }

                }

                const _reject = (err: any) => {
                        if (this.promiseState === this.PENDING) {
                                this.promiseState = this.REJECTED
                                this.promiseResult = err
                                this.onRejectedCallbacks.forEach(callback => callback(err))

                        }
                }
                try {
                        executor(_resolve, _reject)
                } catch (error) {
                        _reject(error)
                }
        }

        then = (onfulfilled?: ((value: unknown) => unknown) | null | undefined, onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined) => {

                let promise = new myPromise((resolve, reject) => {
                        if (this.promiseState === this.FULFILLED) {
                                setTimeout(() => {
                                        try {
                                                if (typeof onfulfilled !== 'function') {
                                                        resolve(this.promiseResult);
                                                } else {
                                                        let x = onfulfilled(this.promiseResult);
                                                        resolvePromise(promise, x, resolve, reject);
                                                }
                                        } catch (e) {
                                                reject(e);
                                        }
                                });
                        } else if (this.promiseState === this.REJECTED) {
                                setTimeout(() => {
                                        try {
                                                if (typeof onrejected !== 'function') {
                                                        reject(this.promiseResult);
                                                } else {
                                                        let x = onrejected(this.promiseResult);
                                                        resolvePromise(promise, x, resolve, reject);
                                                }
                                        } catch (e) {
                                                reject(e)
                                        }
                                });
                        } else if (this.promiseState === this.PENDING) {
                                this.onFulfilledCallbacks.push(() => {
                                        setTimeout(() => {
                                                try {
                                                        if (typeof onfulfilled !== 'function') {
                                                                resolve(this.promiseResult);
                                                        } else {
                                                                let x = onfulfilled(this.promiseResult);
                                                                resolvePromise(promise, x, resolve, reject);
                                                        }
                                                } catch (e) {
                                                        reject(e);
                                                }
                                        });
                                });
                                this.onRejectedCallbacks.push(() => {
                                        setTimeout(() => {
                                                try {
                                                        if (typeof onrejected !== 'function') {
                                                                reject(this.promiseResult);
                                                        } else {
                                                                let x = onrejected(this.promiseResult);
                                                                resolvePromise(promise, x, resolve, reject);
                                                        }
                                                } catch (e) {
                                                        reject(e);
                                                }
                                        });
                                });
                        }
                })
                return promise
        }

        catch = (onrejected: ((reason: any) => void | PromiseLike<void>) | null | undefined) => {
                this.then(undefined, onrejected)
        }

        finally = (callback: (() => void) | null | undefined) => {
                return this.then(callback, callback)
        }

        static resolve = (value: any) => {

                if (value instanceof myPromise) {
                        return value
                } else if (value instanceof Object && "then" in value) {
                        return new myPromise((resolve, reject) => {
                                value.then(resolve, reject)
                        })
                }

                return new myPromise((resolve) => {
                        resolve(value)
                })

        }
        static reject = (err: any) => {
                return new myPromise((resolve, reject) => {
                        reject(err)
                })
        }

        // myPromise.all() 接受一个 promise 的 iterable 类型,Array ,Map , Set 都属于 ES6的 iterable 类型
        static all = (promises: unknown[]) => {
                return new myPromise((resolve, reject) => {
                        if (Array.isArray(promises)) {
                                let result: any = []
                                let count = 0

                                if (promises.length === 0) {
                                        return resolve(promises)
                                }

                                promises.forEach((item, index) => {

                                        myPromise.resolve(item).then(res => {
                                                result[index] = res
                                                count++
                                                count === promises.length && resolve(result)
                                        }, err => {
                                                reject(err)
                                        })

                                })
                        } else {
                                return reject(new TypeError("argument is not iterable"))
                        }
                })
        }

        static allSettled = (promises: unknown[]) => {
                return new myPromise((resolve, reject) => {
                        if (Array.isArray(promises)) {
                                let result: AllSettledType[] = []
                                let count = 0

                                if (promises.length === 0) return resolve(promises)

                                promises.forEach((item, index) => {
                                        myPromise.resolve(item).then(res => {
                                                result[index] = {
                                                        status: "fulfilled",
                                                        value: res
                                                }
                                                count++
                                                count === promises.length && resolve(result)
                                        }, err => {
                                                result[index] = {
                                                        status: "rejected",
                                                        value: err
                                                }
                                                count === promises.length && resolve(result)
                                        })
                                })
                        } else {
                                return reject(new TypeError("argument is not iterable"))
                        }
                })
        }

        static any = (promises: unknown[]) => {
                return new myPromise((resolve, reject) => {
                        if (Array.isArray(promises)) {
                                let errors: unknown[] = []
                                let count = 0
                                if (promises.length === 0) return reject(new Error('All promises were rejected'));

                                promises.forEach((item, index) => {
                                        myPromise.resolve(item).then(res => {
                                                resolve(res)
                                        }, err => {
                                                count++
                                                errors.push(err)
                                                count === promises.length && reject(new Error('All promises were rejected'))
                                        })
                                })
                        } else {
                                return reject(new TypeError("arguments is not iterable"))
                        }
                })
        }

        static race = (promises: unknown[]) => {
                return new myPromise((resolve, reject) => {
                        if (Array.isArray(promises)) {
                                promises.forEach((item, index) => {
                                        myPromise.resolve(item).then(resolve, reject)
                                })
                        } else {
                                return reject(new TypeError("arguments is not iterable"))
                        }
                })
        }
}

function resolvePromise(promise: myPromise, x: any, resolve: Function, reject: (reason: any) => void) {
        // 循环引用
        if (x === promise) {
                throw new TypeError('Chaining cycle detected for promise');
        }

        // 如果 x 是 myPromise , 则 promise 接受 x 的状态,继续执行 x
        if (x instanceof myPromise) {
                x.then(res => {
                        resolvePromise(promise, res, resolve, reject)
                }, reject);
        } else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) {

                try {
                        var then = x.then;
                } catch (e) {
                        return reject(e);
                }

                if (typeof then === 'function') {
                        let called = false;
                        try {
                                then.call(
                                        x,
                                        (y: any) => {
                                                if (called) return;
                                                called = true;
                                                resolvePromise(promise, y, resolve, reject);
                                        },
                                        (r: any) => {
                                                if (called) return;
                                                called = true;
                                                reject(r);
                                        }
                                )
                        } catch (e) {
                                if (called) return;
                                called = true;

                                reject(e);
                        }
                } else {
                        resolve(x);
                }
        } else {
                return resolve(x);
        }
}