手写 promise ( 二 )根据A+规范手写 Promise

108 阅读6分钟

手写 promise ( 二 )根据A+规范手写 Promise

MyPromise

原生 Promise 基础用法

const p = new Promise((resolve,reject)=>{
    resolve('嘿嘿 我成功了')
})

p.then(
    res=>{
        console.log('res',res)
        // res 嘿嘿 我成功了
    },
    rej=>{
        console.log('rej',rej)
    }
)

const p1 = new Promise((resolve,reject)=>{
   throw new Error("错误");
})

p.then(
    res=>{
        console.log('res',res)
    },
    rej=>{
        console.log('rej',rej)
        // rej 错误
    }
)

MyPromise

  • A+ 规范 2.1 得知 promise 有 3 个状态
  • pending 可以转化为其他两个状态 fulfilled 、 rejected
  • fulfilled 不能转化为 rejected 状态,rejected 同理
  • 有 then 方法且可以连续 then
// 成功
const FULFILLED = "FULFILLED";
// 失败
const REJECTED = "REJECTED";
// 等待
const PENDING = "PENDING";

class MyPromise {
  // effect executor 是执行器 第一次 new promise(fn) 的 fn 函数 , 默认直接执行
  constructor(executor) {
    // effect 成功函数
    const resolve = (value) => {
      // effect 避免失败后继续调用 resolve
      if (this.status == PENDING) {
        this.status = FULFILLED;
        this.value = value;
      }
    };

    // effect 失败函数
    const reject = (reason) => {
      // effect 避免成功后继续调用 reject
      if (this.status == PENDING) {
        this.status = REJECTED;
        this.reason = reason;
      }
    };

    // mark 默认是 pending 状态
    this.status = PENDING;
    // mark 成功的值
    this.value = undefined;
    // mark 失败的原因
    this.reason = undefined;

    try {
      // 默认会立刻执行
      executor(resolve, reject);
    } catch (error) {
      // 如果调用执行器抛错了 默认调用 reject 方法
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      if (this.status == FULFILLED) {
        onFulfilled(this.value);
        resolve(this.value);
      }
      if (this.status == REJECTED) {
        onRejected(this.reason);
        reject(this.reason);
      }
    });
  }
}

module.exports = MyPromise;

此时的 MyPromise 已经满足连续 then 和 状态不能随意修改的条件了。

处理 new Promise 中有异步函数的情况

  • 思路
  • 在 MyPromise 内,定义两个队列用以存放 res rej 的函数。
  • MyPromise 有三个状态,当执行 then 时,判断状态是否为 pending。
    • 如果是 pending 说明是异步的。此时将 res rej 加入对应的函数队列中【onFulfilleds、onRejecteds】
    • 因为 executor 函数是异步的,当执行 executor 中的 resolve 时,队列已经添加完毕,resolve 方法中遍历 onFulfilleds执行队列中的函数,reject 同理

案例

const p = new Promise((resolve, reject) => {
 setTimeout(() => {
    resolve('异步函数')
 }, 1000);
});

p.then(
  (res) => {
    console.log("res", res);
  },
  (err) => {
    console.log("err", err);
  }
);
// 一秒后才执行结果
// res 异步函数

显然现在的 MyPromise 满足不了这样的条件,我们只需要加上亿点点条件即可。这里要用到发布订阅模式(不了解的小伙伴要自行查资料)。

优化

class MyPromise{
    constructor(executor) {
        ...
        // mark 利用发布订阅模式实现
        // effect 成功回调函数
        this.onFulfilleds = [];
        // effect 失败回调函数
        this.onRejecteds = [];
        // effect 成功函数
        const resolve = (value) => {
            // effect 避免失败后继续调用 resolve
            if (this.status == PENDING) {
                this.status = FULFILLED;
                this.value = value;
                // 调用 resolve 则需要执行 异步 resolve 方法
                this.onFulfilleds.forEach((fn) => fn());
            }
        };

        // effect 失败函数
        const reject = (reason) => {
            // effect 避免成功后继续调用 reject
            if (this.status == PENDING) {
                this.status = REJECTED;
                this.reason = reason;
                // 调用 reject 则需要执行 异步 reject 方法
                this.onRejecteds.forEach((fn) => fn());
            }
        };
    }
    // mark then 方法有两个参数
    then(onFulfilled, onRejected) {
        // onFulfilled, onRejected 是可选参数
        onFulfilled =
        typeof onFulfilled === "function" ? onFulfilled : (data) => data;

        onRejected =
        typeof onRejected === "function"
            ? onRejected
            : (err) => {
                throw err;
            };
        
        return new MyPromise((resolve, reject) => {
            ...
            // effect 说明是异步方法
            if (this.status === PENDING) {
                // mark 利用函数切片 AOP 思想去包装一次 方便后续添加其他操作
                this.onFulfilleds.push(() => {
                    onFulfilled(this.value);
                    resolve(this.value)
                });
                this.onRejecteds.push(() => {
                    onRejected(this.reason);
                    reject(this.reason)
                });
            }
        })    
    }
}

这样在 new MyPromise 时有异步任务在 then 时也会等到异步任务执行完毕,在执行 then 中的方法拿到成功或失败的信息。

捕获 then res 、rej 内的异常

案例

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("异步函数");
    // throw new Error('错误')
  }, 1000);
});

p.then(
  (res) => {
    console.log("res", res);
    throw new Error("错误");
  },
  (err) => {
    console.log("err", err);
  }
).then(
  (res) => {
    console.log("res1", res);
  },
  (rej) => {
    console.log("err1", rej);
  }
);

优化

...
  // mark then 方法有两个参数
  then(onFulfilled, onRejected) {
    // onFulfilled, onRejected 是可选参数
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (data) => data;

    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };

    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status == FULFILLED) {
        // effect 这里加 try catch 为了 捕获 setTimeout 的异常【无法捕获异步方法的异常 exctor 的try catch 捕获不到异常】
        try {
          let x = onFulfilled(this.value);
          resolve(this.value);
        } catch (error) {
          reject(error);
        }
      }
      if (this.status == REJECTED) {
        try {
          let x = onRejected(this.reason);
          reject(this.reason);
        } catch (error) {
          reject(error);
        }
      }
      // effect 说明是异步方法
      if (this.status === PENDING) {
        // mark 利用函数切片 AOP 思想去包装一次 方便后续添加其他操作
        this.onFulfilleds.push(() => {
          try {
            onFulfilled(this.value);
            resolve(this.value);
          } catch (error) {
            reject(error);
          }
        });
        this.onRejecteds.push(() => {
          try {
            onRejected(this.reason);
            reject(this.reason);
          } catch (error) {
            reject(error);
          }
        });
      }
    });

    return promise2;
  }

then res 返回的是一个 promise

  • 规范 2.3
  • then 处理 new MyPromise 时 对 resolve 进行额外的判断。【 resolvePromise

案例

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("异步函数");
    // throw new Error('错误')
  }, 1000);
});

p.then(
  (res) => {
    console.log("res", res);
    // throw new Error("错误");
    return new Promise((resolve,reject)=>{
        resolve('嘿嘿 我是新的 promise')
    })
  },
  (err) => {
    console.log("err", err);
  }
).then(
  (res) => {
    console.log("res1", res);
  },
  (rej) => {
    console.log("err1", rej);
  }
);

// res 异步函数
// res1 嘿嘿 我是新的 promise

优化

// 成功
const FULFILLED = "FULFILLED";
// 失败
const REJECTED = "REJECTED";
// 等待
const PENDING = "PENDING";

// 因为所有的 promise 都遵循这个规范,规定这里这个写法应该兼容所有的 promise
const resolvePromise = (promise2, x, resolve, reject) => {
  // 判断 x 的值和 promise2 是不是同一个值
  if (promise2 === x) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<Promise>")
    );
  }
  let called;
  // 判断 x 的类型
  if (typeof x == "function" || (typeof x == "object" && x != null)) {
    try {
      // 取then 有可能这个then属性是通过 defineProperty来定义的
      let then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            // 可能还是一个 promise 直到解析结果是一个普通值
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        // 说明是一个普通值
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    // 说明是一个普通值
    resolve(x);
  }
};

const rejectPromise = resolvePromise;

class MyPromise {
  // effect executor 是执行器 第一次 new promise(fn) 的 fn 函数 , 默认直接执行
  constructor(executor) {
    // mark 利用发布订阅模式实现
    // effect 成功回调函数
    this.onFulfilleds = [];
    // effect 失败回调函数
    this.onRejecteds = [];

    // effect 成功函数
    const resolve = (value) => {
      // effect 避免失败后继续调用 resolve
      if (this.status == PENDING) {
        this.status = FULFILLED;
        this.value = value;
        // 调用 resolve 则需要执行 异步 resolve 方法
        this.onFulfilleds.forEach((fn) => fn());
      }
    };

    // effect 失败函数
    const reject = (reason) => {
      // effect 避免成功后继续调用 reject
      if (this.status == PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        // 调用 reject 则需要执行 异步 reject 方法
        this.onRejecteds.forEach((fn) => fn());
      }
    };

    // mark 默认是 pending 状态
    this.status = PENDING;
    // mark 成功的值
    this.value = undefined;
    // mark 失败的原因
    this.reason = undefined;

    try {
      // 默认会立刻执行
      executor(resolve, reject);
    } catch (error) {
      // 如果调用执行器抛错了 默认调用 reject 方法
      reject(error);
    }
  }

  // mark then 方法有两个参数
  then(onFulfilled, onRejected) {
    // onFulfilled, onRejected 是可选参数
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (data) => data;

    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };

    const promise2 = new MyPromise((resolve, reject) => {
      // console.log('promise2',promise2);
      if (this.status === FULFILLED) {
        // effect 这里加 try catch 为了 捕获 setTimeout 的异常【无法捕获异步方法的异常 exctor 的try catch 捕获不到异常】
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            rejectPromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
      // effect 说明是异步方法
      if (this.status === PENDING) {
        // mark 利用函数切片 AOP 思想去包装一次 方便后续添加其他操作
        this.onFulfilleds.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
        this.onRejecteds.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              rejectPromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }
}

// 延时方法 解决封装嵌套的问题
MyPromise.defer = MyPromise.deferred = () => {
  let dfd = {};
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};

module.exports = MyPromise;

至此 MyPromise 已经可以通过 官方的测试案例啦!

后面继续完善 Promise 的各个 API。

PS

  • 代码仓库地址觉得对你有帮助记得 🌟 Star 哦
  • 后续会持续更新相关内容以及其他内容
  • 觉得文章不错的话,记得 👍 🌟 嘻嘻
  • 如有不足欢迎指正