三分钟上手写一个 Promise (Promise A+ 规范)

163 阅读5分钟

首先我们了解一下Promise A+ 规范 promisesaplus.com.cn/ ,这里解释一下 Promise A+ 规范不是我们常说的Promise

  • Promise A+ 规范 是一个社区制定的规范早于ES6 Promise之前,定义了 Promise 的行为和接口
  • ES6 Promise 是 ECMAScript 2015(ES6)标准的一部分,实现了 Promise A+ 规范,但有一些额外的功能和细节上的差异

Promise A+ 规范 的一些关键点

  1. 构造函数

    • Promise 构造函数接受一个执行器函数(executor function),该函数立即执行。
    • 执行器函数接受两个参数:resolve 和 reject,分别用于将 Promise 标记为成功或失败。
  2. 状态

    • 一个 Promise 有三种状态
    • pending(进行中):初始状态,既不是成功也不是失败
    • fulfilled(已成功):操作成功完成
    • rejected(已失败):操作失败
    • 状态不可逆一旦从 pending 状态变为 fulfilled 或 rejected,状态就不会再改变
  3. 实例方法
    - then(onFulfilled, onRejected)Promise A+ 规范里面只有这一个方法,其他的比如下面的 catch finally 都是ES6的Promise新增的。
    - catch(onRejected):相当于 then(null, onRejected),用于捕获前面链中的错误。
    - finally(onFinally):无论 Promise 成功还是失败,都会执行 onFinally 回调。

  4. 静态方法
    - Promise.resolve(value):返回一个以给定值成功完成的 Promise - Promise.reject(reason):返回一个以给定原因失败的 Promise。
    - Promise.all(iterable):等待所有 Promise 完成,如果所有 Promise 都成功,则返回一个包含所有结果的数组;如果有任何一个 Promise 失败,则返回第一个失败的 Promise 的原因。
    - Promise.race(iterable):等待第一个完成的 Promise,并返回其结果或原因。

  5. 错误处理
    - 使用 catch 方法可以更方便地处理错误。
    - 如果没有使用 catch 处理错误,错误会被抛出到全局上下文(如浏览器的 window.onerror)。

接下来我们由浅入深,来实现一个符合 Promise A+ 规范 的构造函数

一. 构造函数实现,不包含then方法

    // 初始化状态
    const PENDING = "pending";
    const FULFILLED = "fulfilled";
    const REJECTED = "rejected";

    class MyPromise {
        // # 声明私有属性避免外部调用
        #state = PENDING;
        #value;
        constructor(executor) {
            const resolve = (value) => {
                this.#setState(FULFILLED, value);
            };
            const reject = (error) => {
                this.#setState(REJECTED, error);
            };
            // 错误捕获,注意这里捕获不到异步的错误,原生Promise也捕获不到
            try {
                executor(resolve, reject);
            } catch (error) {
                reject(error);
            }
        }
        #setState(state, value) {
            if (this.#state !== PENDING) return; // 状态是不可逆的
            this.#state = state;
            this.#value = value;
        }
    }
  • 验证state,value的值是否正确
  • state是否不可逆
  • 错误是否捕获到
    const p = new MyPromise((resolve, reject) => {
        resolve(1);
        throw new Error(2222)
    });
    console.log("p", p);

二. 实现then方法

then方法 是Promise A+ 规范里面的核心,也只有这一个方法,我们看下先怎么使用

    const p = new Promise((resolve, reject) => {
        resolve("success");
        reject("error");
    });
    p.then(
        (res) => {
        // 成功的回调
            console.log("res", res);
        },
        (error) => {
        // 失败的回调
            console.log("error", error);
        }.then(
        // 支持链式调用,而且在链式调用过程中状态也是可以改变的,比如发生错误调用catch捕获
            (res) => {
                console.log("第二次 res", res);
            },
            (error) => {
                console.log("第二次 error", error);
            }
        )
    );

实现

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
    // # 声明私有属性避免外部调用
    #state = PENDING;
    #value;
    #handlers = [];
    constructor(executor) {
        const resolve = (value) => {
            this.#setState(FULFILLED, value);
        };
        const reject = (error) => {
            this.#setState(REJECTED, error);
        };
        // 错误捕获,注意这里捕获不到异步的错误,原生Promise也捕获不到
        try {
            // 接受一个执行器函数该函数立即执行
            // 执行器函数接受两个参数:resolve 和 reject,分别用于将 Promise 标记为成功或失败
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }
    #setState(state, value) {
        if (this.#state !== PENDING) return; // 状态是不可逆的
        this.#state = state;
        this.#value = value;
        this.#ranTask();
    }
    #ranTask() {
        if (this.#state !== PENDING) {
            this.#handlers.forEach((cb) => cb());
            this.#handlers = []; // 执行完清空
        }
    }
    then(onFulfilled, onRejected) {
        // 支持链式调用
        return new MyPromise((resolve, reject) => {
            // 保存每次then调用时候的回调函数,状态改变的时候一起执行
            this.#handlers.push(() => {
                try {
                    if (this.#state === FULFILLED) {
                        // 链式调用的返回值
                        const res = onFulfilled(this.#value);
                        resolve(res);
                    }
                    if (this.#state === REJECTED) {
                        // 链式调用的返回值,注意这里也是resolve,返回的还是成功的promise
                        const error = onRejected(this.#value);
                        resolve(error);
                    }
                } catch (error) {
                    reject(error);
                }
            });
            this.#ranTask();
        });
    }
}
// 测试链式调用
const p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
    }, 10);
});
p.then(
    (res) => {
        console.log("第一次 res", res);
    },
    (error) => {
        console.log("第一次 error", error);
    }
);
p.then(
    (res) => {
        console.log("第二次 res", res);
        return 3;
    },
    (error) => {
        console.log("第二次 error", error);
    }
).then((res) => {
    console.log("第三次 res", res);
});

三. 完善then方法

上面then方法还遗留一些问题,我们来解决一下

  • 如果 onFulfilled 不是函数且 promise1 成功完成, promise2 必须成功完成并返回相同的值
  • then方法是异步调用
  • 如果 then方法返回值是一个 promiseLike对象(有 then 方法且看上去像一个 promise),promiseLike对象 的完成值,而不是这个 promiseLike对象本身

完善之前我们先写一个 isPromiseLike 函数

  • 如果一个值的类型为 object 或者 function
  • 该值还存在一个then方法
  • 该值就是一个 PromiseLike 对象
function isPromiseLike(obj) {
    // 对象有then方法
    return typeof obj?.then === "function";
}

现在开始完善then方法

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
    // # 声明私有属性避免外部调用
    #state = PENDING;
    #value;
    #handlers = [];
    constructor(executor) {
        const resolve = (value) => {
            this.#setState(FULFILLED, value);
        };
        const reject = (error) => {
            this.#setState(REJECTED, error);
        };
        // 错误捕获,注意这里捕获不到异步的错误,原生Promise也捕获不到
        try {
            // 接受一个执行器函数该函数立即执行
   // 执行器函数接受两个参数:resolve 和 reject,分别用于将 Promise 标记为成功或失败
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }
    #setState(state, value) {
        if (this.#state !== PENDING) return; // 状态是不可逆的
        this.#state = state;
        this.#value = value;
        this.#ranTask();
    }
    #ranTask() {
        // 加入微任务队列,考虑兼容问题可以自己在写一个方法
        queueMicrotask(() => {
            if (this.#state !== PENDING) {
                this.#handlers.forEach((cb) => cb());
                this.#handlers = []; // 执行完清空
            }
        });
    }
    then(onFulfilled, onRejected) {
        // 支持链式调用
        return new MyPromise((resolve, reject) => {
            // 保存每次then调用时候的回调函数,状态改变的时候一起执行
            this.#handlers.push(() => {
                try {
                    const cb = this.#state === FULFILLED ? onFulfilled : onRejected;
                    // 如果 onFulfilled 不是函数
                    const res = typeof cb === "function" ? cb(this.#value) : this.#value;
                    if (isPromiseLike(res)) {
                        res.then(resolve, reject);
                    } else {
                        resolve(res);
                    }
                } catch (error) {
                    reject(error);
                }
            });
            this.#ranTask();
        });
    }
}
// 测试链式调用
const p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
    }, 10);
});
// 测试 onFulfilled 不是函数
p.then(null, (error) => {
    console.log("第一次 error", error);
}).then((res) => {
    console.log("第一次 res", res);
});

// then 返回 promiseLike对象
const p1 = p.then((res) => {
    return new MyPromise((resolve, reject) => {
        resolve(2);
        // reject(2);
    });
});
p1.then((res) => {
    console.log("p1 ===", res);
},(error) => {
    console.log("p1 error ===", error);
});