手写promise实现

20 阅读1分钟
        const PROMISE_STATUS_PENDING = 'pending';
        const PROMISE_STATUS_FULFILLED = 'fulfilled';
        const PROMISE_STATUS_REJECTED = 'rejected';

        function execFunctionWithCatchError(execFun, value, resolve, reject) {
            try {
                const result = execFun(value);
                resolve(result);
            }
            catch (error) {
                reject(error);
            }
        }

        class MyPromise {
            constructor(executor) {
                this.status = PROMISE_STATUS_PENDING;
                this.value = undefined;
                this.reason = undefined;
                this.onFulfilledFns = [];
                this.onRjectedFns = [];

                const resolve = value => {
                    if (this.status === PROMISE_STATUS_PENDING) {
                        queueMicrotask(() => {
                            if (this.status !== PROMISE_STATUS_PENDING) {
                                return;
                            }
                            this.status = PROMISE_STATUS_FULFILLED;
                            this.value = value;
                            this.onFulfilledFns.forEach(fn => {
                                fn(this.value);
                            });
                        });
                    }
                };

                const reject = reason => {
                    if (this.status === PROMISE_STATUS_PENDING) {
                        queueMicrotask(() => {
                            if (this.status !== PROMISE_STATUS_PENDING) {
                                return;
                            }
                            this.status = PROMISE_STATUS_REJECTED;
                            this.reason = reason;
                            this.onRjectedFns.forEach(fn => {
                                fn(this.reason);
                            });
                        });
                    }
                };

                try {
                    executor(resolve, reject);
                } catch (error) {
                    reject(error);
                }
            }

            then(onFulfilled, onRjected) {
                onFulfilled = onFulfilled || (value => {
                    return value;
                });

                onRjected = onRjected || (err => {
                    throw err;
                });

                return new MyPromise((resolve, reject) => {
                    if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
                        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
                    }
                    if (this.status === PROMISE_STATUS_REJECTED && onRjected) {
                        execFunctionWithCatchError(onRjected, this.value, resolve, reject);
                    }

                    if (this.status === PROMISE_STATUS_PENDING) {
                        if (onFulfilled) {
                            this.onFulfilledFns.push(() => {
                                execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
                            });
                        }

                        if (onRjected) {
                            this.onRjectedFns.push(() => {
                                execFunctionWithCatchError(onRjected, this.reason, resolve, reject);
                            });
                        }
                    }
                })
            }

            catch(onRjected) {
                return this.then(undefined, onRjected);
            }

            finally(onFinally) {
                this.then(
                    () => {
                        onFinally();
                    },
                    () => {
                        onFinally();
                    }
                )
            }

            static resolve(value) {
                return new MyPromise(resolve = resolve(value));
            }

            static reject(reason) {
                return new MyPromise(resolve, reject = reject(reason));
            }

            static all(promises) {
                return new MyPromise((resolve, reject) => {
                    const values = [];
                    promises.forEach(promise => {
                        promise.then(res => {
                            values.push(res);
                            if (values.length === promises.length) {
                                resolve(values);
                            }
                        }, 
                        err => {
                            reject(err);
                        });
                    })
                })
            }

            static allSettled(promises) {
                return new MyPromise(resolve => {
                    const results = [];
                    promises.forEach(promise => {
                        promise.then(
                            res => {
                                results.push({status: PROMISE_STATUS_FULFILLED, value: res});
                                if (results.length === promises.length) {
                                    resolve(results);
                                }
                            },
                            err => {
                                results.push({
                                    status: PROMISE_STATUS_REJECTED, value: err
                                });
                                if (results.length === promises.length) {
                                    resolve(results);
                                }
                            }
                        )
                    })
                })
            }

            static race(promises) {
                return new MyPromise((resolve, reject) => {
                    promises.forEach(promise => {
                        promise.then(res => {
                            resolve(res);
                        }, err => {
                            reject(err);
                        })
                    })
                })
            }

            static any(promises) {
                return new MyPromise((resolve, reject) => {
                    const reasons = [];
                    promises.forEach(promise => {
                        promise.then(
                            res => {
                                resolve(res);
                            },
                            err => {
                                reasons.push(err);
                                if (reasons.length === promise.length) {
                                    reject(reasons);
                                }
                            }
                        )
                    })
                })
            }
        }

        const p1 = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                console.log('---- 1');
                resolve(111);
            });
        }).then(res => {
            console.log('p1 res :>>', res);
        });

        const p2 = new MyPromise((resolve, reject) => {
            console.log('---2');
            resolve(2);
        });

        const p3 = new MyPromise((resolve, reject) => {
            console.log('---3');
            resolve(333);
        });

        const p4 = new MyPromise((resolve, reject) => {
            console.log('---4');
            reject(444);
        });

        MyPromise.all([p2, p3]).then(res => {
            console.log('p2 & p3 res :>>', res);
        });

        MyPromise.all([p2, p4]).then(res => {
            console.log('p2&p4 res :>>', res);
        }).catch(err => {
            console.log('err :..', err);
        });