根据MDN手写Promise

90 阅读6分钟

前言

  1. Promise 之前需要知道它是什么

MDN

Promise 对象表示异步操作最终的完成(或失败)以及其结果值。Promise 对象表示异步操作最终的完成(或失败)以及其结果值。

  1. 知道 Promise 是什么了之后需要知道它怎么使用 还有它的一些静态方法

Promise - JavaScript | MDN (mozilla.org)

使用 Promise - JavaScript | MDN (mozilla.org)


实现

简单的使用方法:

const promise = new Promise((resolve, reject) => {
    const res = Math.floor(Math.random() * 2);
    if (res) {
        resolve(res);
    } else {
        reject(res);
    }
})
console.log(promise);
promise.then(value => {
    console.log(value);
}).catch(reason => {
    console.log(reason);
}).finally(() => {
    console.log('finally');
});

在浏览器执行这段代码后发现其中需要注意的点是

  • Promise 中的状态
    • PENDING
      • 初始化的状态
    • FULFILLED
      • 执行resolve后的状态
    • REJECTED
      • 执行reject后的状态
  • 传入的立即执行方法excutor
    • 正确执行时的resolve
    • 错误时执行的reject
  • 实例中的三个异步方法
    • then
    • catch
    • finally
  • 链式调用

实现then catch finally resolve reject

  1. 确定 Promise 中的三种状态以及立即执行方法
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {
        this.status = PENDING;
        // 成功值和失败的原因在初始化时都为undefined
        this.value = undefined;
        this.reason = undefined;

        executor(this.reslove, this.reject);
    }

    resolve(value) {
        if (this.status === PENDING) {
            this.status = FULFILLED;
            this.value = value;
        }
    }

    reject(reason) {
        if (this.status === PENDING) {
            this.status === REJECTED;
            this.reason = reason;
        }
    }
}
  1. 但是根据MDN文档显示Promise中有resolvereject两个静态方法 并且返回一个Promise实例 具体详见MDN

Promise.resolve() - JavaScript | MDN (mozilla.org)
Promise.reject() - JavaScript | MDN (mozilla.org)

class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;

    executor(this.#privateResolve, this.#privateReject);
  }

  #privateResolve(value) {
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
    }
  }

  #privateReject(reason) {
    if (this.status === PENDING) {
      this.status === REJECTED;
      this.reason = reason;
    }
  }

  // value 可以是一个任意值或者是一个Promise
  // 静态方法返回一个状态FULFILLED的Promise
  static resolve(value) {
    return value instanceof Promise
      ? value
      : new Promise((resolve) => resolve(value));
  }

  // 返回一个状态是REJECTED的Promise
  static reject(reason) {
    return new Promise((resolve, reject) => reject(reason));
  }
}
  1. 实现then catch finally方法
  • 三个方法都是异步方法 需要将后续执行方法保存, 等异步执行完毕后继续执行。
  • 三个方法都是链式调用 需要返回一个Promise实例 (暂时不实现)
  • finally
    • 状态改变后执行 如果状态不变则不执行
class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.fulfilledCallback = undefined;
        this.rejectedCallback = undefined;
        this.onFinallyCallback = undefined;

        executor(this.#privateResolve, this.#privateReject);
    }

    #privateResolve = (value) => {
        if (this.status === PENDING) {
            this.status = FULFILLED;
            this.value = value;
            this.fulfilledCallback?.(value);
            this.onFinallyCallback?.();
        }
    }

    #privateReject = (reason) => {
        if (this.status === PENDING) {
            this.status === REJECTED;
            this.reason = reason;
            this.rejectedCallback?.(reason);
            this.onFinallyCallback?.();
        }
    }

    // 在执行onfulfilled方法时需要进行类型检查 如果类型不对或者没传 则修改为默认方法
    privateCheckOnFulFilled(onFulfilled) {
        return typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
    }

    // 执行onreject方法前也需要进行类型检查 如果未通过则改为默认方法
    privateCheckOnRejected(onRejected) {
        return typeof onRejected === 'function'
            ? onRejected
            : (reason) => {
                throw reason;
            };
    }

    // finally方法也是如此 默认方法为空方法
    privateCheckOnFinally(onFinally) {
        return typeof onFinally === 'function' ? onFinally : () => { };
    }

    ...

    then(onFulfilled, onRejected) {
        onFulfilled = this.privateCheckOnFulFilled(onFulfilled);
        onRejected = this.privateCheckOnRejected(onRejected);
        // 根据当前状态判定应该异步执行还是‘同步’执行
        switch (this.status) {
            case FULFILLED:
                this.fulfilledCallback(this.value);
                break;
            case REJECTED:
                this.rejectedCallback(this.reason);
                break;
            case PENDING:
            default:
                this.fulfilledCallback = onFulfilled;
                this.rejectedCallback = onRejected;
        }
    }

    catch(onRejected) {
        onRejected = this.privateCheckOnRejected(onRejected);
        switch (this.status) {
            case REJECTED:
                onRejected(this.reason);
                break;
            case FULFILLED:
                break;
            case PENDING:
                this.rejectedCallback = onRejected;
        }
    }

    finally(onFinally) {
        onFinally = this.privateCheckOnFinally(onFinally);
        switch (this.status) {
            case PENDING:
                this.onFinallyCallback = onFinally;
                break;
            case REJECTED:
            case FULFILLED:
            default:
                onFinally();
        }
    }
}
  1. 实现then的链式调用
  • 上一个then方法中return的值是下一个then方法中的参数

class Promise {
    ...

    then(onFulfilled, onRejected) {
        onFulfilled = this.privateCheckOnFulFilled(onFulfilled);
        onRejected = this.privateCheckOnRejected(onRejected);
        const promise = new Promise((resolve, reject) => {
            let result = undefined;
            let reason = undefined;
            try {
                switch (this.status) {
                    case FULFILLED:
                        result = this.fulfilledCallback(this.value);
                        resolve(result);
                        break;
                    case REJECTED:
                        reason = this.rejectedCallback(this.reason);
                        reject(err);
                        break;
                    case PENDING:
                    default:
                        this.fulfilledCallback = (value) => {
                            try {
                                const result = onFulfilled(value);
                                resolve(result);
                            } catch(reson) {
                                reject(reson);
                            }
                        };
                        this.rejectedCallback = onRejected;
                }
            } catch(err) {
                reject(err);
            }
        })
        return promise;
    }
    
    ...
}
  1. 实现catchfinally的链式调用
  • catch的注意事项
  • finally不会接收参数
    • finally类似于调用 Promise.prototype.then(onFinally, onFinally)
    • finally不接收参数
    • 不会改变promise的状态
      • 与 Promise.resolve(2).then(() => 77, () => {}) 不同,它返回一个最终会兑现为值 77 的 promise,而 Promise.resolve(2).finally(() => 77) 返回一个最终兑现为值 2 的 promise。
      • 类似地,与 Promise.reject(3).then(() => {}, () => 88) 不同,它返回一个最终兑现为值 88 的 promise,而 Promise.reject(3).finally(() => 88) 返回一个最终以原因 3 拒绝的 promise。
    class Promise {
        catch(onRejected) {
            return this.then(undefined, onRejected);
        }

        finally(onFinally) {
            onFinally = this.privateCheckOnFinally(onFinally);
            return this.then(
                // 不能修改当前promise的状态
                (value) => Promise.resolve(onFinally()).then(() => value),
                (reason) =>
                    Promise.resolve(onFinally()).then(() => {
                        throw reason;
                    })
            );
        }
    }

其他静态方法

Promise.all()

Promise.all的使用方法以及规则

Promise.all() - JavaScript | MDN (mozilla.org)

  1. 返回一个promise
  2. 如果传入的 promise迭代 中只要有一个状态变为REJECTED,则 Promise.all 的状态也变为 REJECTED
class Promise {
    ...
    
    // all的入参是一个可迭代对象
    static all(iterable) {
        return new Promise((resolve, reject) => {
            const promiseArray = Array.from(iterable);
            const size = promiseArray.length;
            const res = new Array(size).fill();
            let index = 0;
            for (let i = 0; i < size; i++) {
                Promise.resolve(promiseArray[i]).then(value => {
                Promise.resolve(promiseArray[i]).then(value => {
                    res[i] = value;
                    index++;
                    if (index === size) {
                        resolve(res);
                    }
                },
                reason => {
                    reject(reason);
                })
            }
        })
    }
}

Promise.allSettled()

Promise.allSettled的使用方法以及规则

Promise.allSettled() - JavaScript | MDN (mozilla.org)

  1. 和all基本相同 不同的是返回值和不论迭代结果是REJECTED还是FULFILLED
class Promise {
    ...
    
    static allSettled(iterable) {
        return new Promise3((resolve, reject) => {
            const promiseArray = Array.from(iterable);
            const size = promiseArray.length;
            const res = new Array(size).fill();
            let index = 0;
            for (let i = 0; i < size; i++) {
                Promise.resolve(promiseArray[i]).then(value => {
                Promise3.resolve(promiseArray[i]).then(value => {
                    res[i] = {
                        status: 'fulfilled',
                        value
                    };
                    index++;
                    if (index === size) {
                        resolve(res);
                    }
                },
                reason => {
                    res[i] = {
                        status: 'rejected',
                        reason
                    };
                    index++;
                    if (index === size) {
                        resolve(res);
                    }
                })
            }
        })
    }
}

Promise.race()

Promise.race的使用方法以及规则

Promise.race() - JavaScript | MDN (mozilla.org)

  1. 取最快结果 不管结果状态是什么
  2. 参数是一个promise的可迭代对象
class Promise {
    ...
    
    static race(iterable) {
        return new Promise((resolve, reject) => {
            const promiseArray = Array.from(iterable);
            const size = promiseArray.length;
            for (let i = 0; i < size; i++) {
                promiseArray[i].then(value => {
                    resolve(value);
                },
                reason => {
                    reject(reason);
                })
            }
        })
    }
}

Promise.any()

Promise.any的使用方法以及规则

Promise.any() - JavaScript | MDN (mozilla.org)

  1. race相似 不同的是只返回状态为FULFILLED的值 如果全部是REJECTED 则返回一个错误
class Promise {
    ...
    
    static any(iterable) {
        return new Promise((resolve, reject) => {
            const promiseArray = Array.from(iterable);
            const size = promiseArray.length;
            const errRes = new Array(size);
            let index = 0;
            for (let i = 0; i < size; i++) {
                promiseArray[i].then(value => {
                    resolve(value);
                },
                reason => {
                    index++;
                    if (index === size) {
                        reject('AggregateError: All promises were rejected');
                    }
                })
            }
        })
    }
}

总结

MDN nb 更完善的可以参照 - core-js 中 Promise 的 Polyfill

完整代码

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.fulfilledCallback = undefined;
        this.rejectedCallback = undefined;
        this.onFinallyCallback = undefined;

        executor(this.#privateResolve, this.#privateReject);
    }

    #privateResolve = (value) => {
        if (this.status === PENDING) {
            this.status = FULFILLED;
            this.value = value;
            this.fulfilledCallback?.(value);
            this.onFinallyCallback?.();
        }
    }

    #privateReject = (reason) => {
        if (this.status === PENDING) {
            this.status === REJECTED;
            this.reason = reason;
            this.rejectedCallback?.(reason);
            this.onFinallyCallback?.();
        }
    }

    #checkOnFulFilled(onFulfilled) {
        return typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
    }

    #checkOnRejected(onRejected) {
        return typeof onRejected === 'function'
            ? onRejected
            : (reason) => {
                throw reason;
            };
    }

    #checkOnFinally(onFinally) {
        return typeof onFinally === 'function' ? onFinally : () => { };
    }

    static resolve(value) {
        return value instanceof Promise
            ? value
            : new Promise((resolve) => resolve(value));
    }

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

    then(onFulfilled, onRejected) {
        onFulfilled = this.#checkOnFulFilled(onFulfilled);
        onRejected = this.#checkOnRejected(onRejected);
        const promise = new Promise((resolve, reject) => {
            let result = undefined;
            let reason = undefined;
            try {
                switch (this.status) {
                    case FULFILLED:
                        result = this.fulfilledCallback(this.value);
                        resolve(result);
                        break;
                    case REJECTED:
                        reason = this.rejectedCallback(this.reason);
                        reject(err);
                        break;
                    case PENDING:
                    default:
                        this.fulfilledCallback = (value) => {
                            try {
                                const result = onFulfilled(value);
                                resolve(result);
                            } catch (reson) {
                                reject(reson);
                            }
                        };
                        this.rejectedCallback = onRejected;
                }
            } catch (err) {
                reject(err);
            }
        })
        return promise;
    }

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

    finally(onFinally) {
        onFinally = this.#checkOnFinally(onFinally);
        return this.then(
            (value) => Promise.resolve(onFinally()).then(() => value),
            (reason) =>
                Promise.resolve(onFinally()).then(() => {
                    throw reason;
                })
        );
    }

    static all(iterable) {
        return new Promise((resolve, reject) => {
            const promiseArray = Array.from(iterable);
            const size = promiseArray.length;
            const res = new Array(size);
            let index = 0;
            for (let i = 0; i < size; i++) {
                Promise.resolve(promiseArray[i]).then(value => {
                    res[i] = value;
                    index++;
                    if (index === size) {
                        resolve(res);
                    }
                },
                reason => {
                    reject(reason);
                })
            }
        })
    }

    static allSettled(iterable) {
        return new Promise((resolve, reject) => {
            const promiseArray = Array.from(iterable);
            const size = promiseArray.length;
            const res = new Array(size);
            let index = 0;
            for (let i = 0; i < size; i++) {
                Promise.resolve(promiseArray[i]).then(value => {
                    res[i] = {
                        status: 'fulfilled',
                        value
                    };
                    index++;
                    if (index === size) {
                        resolve(res);
                    }
                },
                reason => {
                    res[i] = {
                        status: 'rejected',
                        reason
                    };
                    index++;
                    if (index === size) {
                        resolve(res);
                    }
                })
            }
        })
    }

    static race(iterable) {
        return new Promise((resolve, reject) => {
            const promiseArray = Array.from(iterable);
            const size = promiseArray.length;
            for (let i = 0; i < size; i++) {
                promiseArray[i].then(value => {
                    resolve(value);
                },
                reason => {
                    reject(reason);
                })
            }
        })
    }

    static any(iterable) {
        return new Promise((resolve, reject) => {
            const promiseArray = Array.from(iterable);
            const size = promiseArray.length;
            const errRes = new Array(size);
            let index = 0;
            for (let i = 0; i < size; i++) {
                promiseArray[i].then(value => {
                    resolve(value);
                },
                reason => {
                    index++;
                    if (index === size) {
                        reject('AggregateError: All promises were rejected');
                    }
                })
            }
        })
    }
}