手写一个Promise

268 阅读3分钟

Promise/A+

下面是一个简单的Promise的实例

const promise1 = new Promise((resolve, reject) => {
    console.log(1);
    resolve(2);
    console.log(3);
    reject(4);
});

const promise2 = new Promise((resolve, reject) => {
    reject(5);
});

console.log(6);

promise1.then(console.log, console.log);
promise2.then(console.log, console.log);

它对应的输出结果为

1
3
6
2
5

结合Promise/A+规范,可以总结出Promise基本特征如下所示(promise表示Promise的实例)

  1. new Promise时,需要传入一个立即执行函数executor
  2. executor接受两个异步执行的回调函数,分别是成功后的回调resolve和失败后的回调reject
  3. promise只有三个状态,pendingfulfilledrejected
  4. promise初始状态为pending
  5. promise只能从pending修改为fulfilled或者rejected,修改以后不可逆,无法再次修改
  6. promise有一个value属性,用来保存成功后返回的值
  7. promise有一个reason属性,用来保存失败后返回的值
  8. promise有一个then方法,接收 Promise成功的回调onFulfilledPromise失败的回调onRejected

Promise 基本结构

下面是一个 Promise 的基本结构

const PromiseStatusEnum = {
    PENDING: 'pending',
    FULFILLED: 'fulfilled',
    REJECTED: 'rejected'
};

class MyPromise {
    constructor(executor) {
        this.status = PromiseStatusEnum.PENDING;
        this.value = null;
        this.reason = null;
        this.resolveQueue = [];
        this.rejectQueue = [];
    }

    then = (onFulfilled, onRejected) => {};

    catch = (onRejected) => {};

    finally = (callback) => {};

    static resolve = (value) => {};

    static reject = (error) => {};

    static all = (promiseArr) => {};

    static race = (promiseArr) => {};
}

executor

executor接受两个回调函数resolvereject,通过箭头函数绑定当前实例的this,通过setTimeout异步执行

const resolve = (value) => {
    const run = () => {};
    setTimeout(run);
};

const reject = (error) => {
    const run = () => {};
    setTimeout(run);
};

run主要负责

  1. 判断当前的状态是否为pending,并对其修改
  2. 记录成功或者失败的返回值

基本的executor如下

const resolve = (value) => {
    const run = () => {
        if (this.status === PromiseStatusEnum.PENDING) {
            this.status = PromiseStatusEnum.FULFILLED;
            this.value = value;
        }
    };
    setTimeout(run);
};

const reject = (error) => {
    const run = () => {
        if (this.status === PromiseStatusEnum.PENDING) {
            this.status = PromiseStatusEnum.REJECTED;
            this.reason = error;
        }
    };
    setTimeout(run);
};

//兼容executor的异常情况
try {
    executor(resolve, reject);
} catch (e) {
    reject(e);
}

then

then接受两个回调函数onFulfilledonRejected,并返回一个Promise对象

then = (onFulfilled, onRejected) => {
    // onFulfilled或者onRejected不是函数时,返回当前的值
    typeof onFulfilled !== 'function' ? (onFulfilled = (value) => value) : null;
    typeof onRejected !== 'function' ? (onRejected = (error) => error) : null;

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

再根据promise的状态进行不同的操作

  1. state === 'fulfilled'时,执行 onFulfilled
  2. state === 'rejected'时,执行 onRejected
  3. 如果在执行回调函数时抛出了异常,那么就会把这个异常作为参数,直接reject
  4. 回调函数返回的值不是Promise,直接resolve
  5. 回调函数返回的值是Promise,调用then方法,保证Promise会被全部执行
  6. state === 'pending'时,需要将回调函数放入队列中,等待执行
then = (onFulfilled, onRejected) => {
    return new MyPromise((resolve, reject) => {
        const resolveFn = (value) => {
            try {
                const x = onFulfilled(value);
                x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
            } catch (error) {
                reject(error);
            }
        };

        const rejectFn = (error) => {
            try {
                const x = onRejected(error);
                x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
            } catch (error) {
                reject(error);
            }
        };

        switch (this.status) {
            case PromiseStatusEnum.PENDING:
                this.resolveQueue.push(resolveFn);
                this.rejectQueue.push(rejectFn);
                break;
            case PromiseStatusEnum.FULFILLED:
                resolveFn(this.value);
                break;
            case PromiseStatusEnum.REJECTED:
                rejectFn(this.reason);
                break;
        }
    });
};

resolveQueuerejectQueue收集的回调,需要在executor中的resolvereject执行

const resolve = (value) => {
    const run = () => {
        if (this.status === PromiseStatusEnum.PENDING) {
            this.status = PromiseStatusEnum.FULFILLED;
            this.value = value;

            while (this.resolveQueue.length) {
                const callback = this.resolveQueue.shift();
                callback(value);
            }
        }
    };

    setTimeout(run);
};
const reject = (error) => {
    const run = () => {
        if (this.status === PromiseStatusEnum.PENDING) {
            this.status = PromiseStatusEnum.REJECTED;
            this.reason = error;

            while (this.rejectQueue.length) {
                const callback = this.rejectQueue.shift();
                callback(error);
            }
        }
    };

    setTimeout(run);
};

catch

实现了then以后,catch就比较简单了,直接调用thenonFulfilled传空就行了

catch = (onRejected) => this.then(undefined, onRejected);

finally

finally接受一个回调函数,在promise结束时,无论结果是fulfilled或者是rejected,都会执行该回调函数

finally = (callback) =>
    this.then(
        (value) => MyPromise.resolve(callback()).then(() => value),
        (error) => MyPromise.resolve(callback()).then(() => MyPromise.reject(error))
    );

完整代码

还有一些静态方法,resolverejectallreace等,基本实现了Promise以后,就比较简单了,不在赘述了

const PromiseStatusEnum = {
    PENDING: 'pending',
    FULFILLED: 'fulfilled',
    REJECTED: 'rejected'
};

class MyPromise {
    constructor(executor) {
        this.status = PromiseStatusEnum.PENDING;
        this.value = null;
        this.reason = null;
        this.resolveQueue = [];
        this.rejectQueue = [];

        const resolve = (value) => {
            const run = () => {
                if (this.status === PromiseStatusEnum.PENDING) {
                    this.status = PromiseStatusEnum.FULFILLED;
                    this.value = value;

                    while (this.resolveQueue.length) {
                        const callback = this.resolveQueue.shift();
                        callback(value);
                    }
                }
            };

            setTimeout(run);
        };
        const reject = (error) => {
            const run = () => {
                if (this.status === PromiseStatusEnum.PENDING) {
                    this.status = PromiseStatusEnum.REJECTED;
                    this.reason = error;

                    while (this.rejectQueue.length) {
                        const callback = this.rejectQueue.shift();
                        callback(error);
                    }
                }
            };

            setTimeout(run);
        };

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

    then = (onFulfilled, onRejected) => {
        return new MyPromise((resolve, reject) => {
            const resolveFn = (value) => {
                try {
                    const x = onFulfilled(value);
                    x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
                } catch (error) {
                    reject(error);
                }
            };

            const rejectFn = (error) => {
                try {
                    const x = onRejected(error);
                    x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
                } catch (error) {
                    reject(error);
                }
            };

            switch (this.status) {
                case PromiseStatusEnum.PENDING:
                    this.resolveQueue.push(resolveFn);
                    this.rejectQueue.push(rejectFn);
                    break;
                case PromiseStatusEnum.FULFILLED:
                    resolveFn(this.value);
                    break;
                case PromiseStatusEnum.REJECTED:
                    rejectFn(this.reason);
                    break;
            }
        });
    };

    catch = (onRejected) => this.then(undefined, onRejected);

    finally = (callback) =>
        this.then(
            (value) => MyPromise.resolve(callback()).then(() => value),
            (error) => MyPromise.resolve(callback()).then(() => MyPromise.reject(error))
        );

    static resolve = (value) =>
        new MyPromise((resolve, reject) =>
            value instanceof MyPromise ? value : new MyPromise((resolve, reject) => resolve(value))
        );

    static reject = (error) => new MyPromise((resolve, reject) => reject(error));

    // 静态all方法
    static all = (promiseArr) => {
        let count = 0;
        let result = [];
        return new MyPromise((resolve, reject) => {
            if (!promiseArr.length) {
                return resolve(result);
            }
            promiseArr.forEach((p, i) => {
                MyPromise.resolve(p).then(
                    (value) => {
                        count++;
                        result[i] = value;
                        if (count === promiseArr.length) {
                            resolve(result);
                        }
                    },
                    (error) => {
                        reject(error);
                    }
                );
            });
        });
    };

    // 静态race方法
    static race = (promiseArr) =>
        new MyPromise((resolve, reject) => {
            promiseArr.forEach((p) => {
                MyPromise.resolve(p).then(
                    (value) => {
                        resolve(value);
                    },
                    (error) => {
                        reject(error);
                    }
                );
            });
        });
}

const promise1 = new MyPromise((resolve, reject) => {
    console.log(1);
    resolve(2);
    console.log(3);
    reject(4);
});

const promise2 = new MyPromise((resolve, reject) => {
    reject(5);
});

console.log(6);

promise1.then(console.log, console.log);
promise2.then(console.log, console.log);

它对应的输出结果与之前一致

1
3
6
2
5