手写Promise特性全解析,手把手实现(二)

前言

上一篇我们已经完成了 Promise 的基本实现:包括 Promise的类创建,状态设定,resovle、reject、then方法实现,并处理了Promise实例化中异步情况,then 方法返回 promise实例的情况,接下来我们将继续完成 Promise 类中的一些其他方法

如果有没有看过的,可以通过 下面 链接进行查看


在这里插入图片描述

正文开始如果觉得文章对您有帮助,请帮我三连+订阅,谢谢💖💖💖


手写逻辑

为了避免篇幅过长,我们将案例分为两张来写,本章实现功能

  • 实例方法 catch 的实现
  • 静态方法 resolve 返回一个 promise
  • 实例方法 finally 方法 不管成功失败都会执行一次
  • 静态方法 all 方法的实现, 只有全部成功会返回所有的结果,任意一个失败就返回失败
  • 静态方法 race 方法的实现, 返回最先成功的promise结果
  • 静态方法 allSettled 方法的实现, 等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

1. catch 实例方法实现

  • catch 是一个实例方法,用于处理 Promise 被拒绝的情况。
  • 它接受一个 onRejected 回调函数作为参数,当 Promise 被拒绝时,将调用该函数。
  • catch 返回一个新的 Promise 对象,如果 onRejected 回调函数返回的是一个值或新的 Promise,那么返回的 Promise 将解析为该值或新的 Promise 的结果。

代码实例

我们需要对 then 方法中的代码进行异常捕获,不然,后面的代码会因为异常终止执行

then() {
    //... 省略一些代码

 let p = new Promise((resolve, reject) => {
      if (this.state === FULFILLED) {
        queueMicrotask(() => {
          try {
            let res = onFulfilled(this.value);
            resolveRes(res, p, resolve, reject);
          } catch (error) {
            reject(error.message);
          }
        });
      } else if (this.state === REJECTED) {
        queueMicrotask(() => {
          try {
            let res = onRejected(this.reason);
            resolveRes(res, p, resolve, reject);
          } catch (error) {
            reject(error.message);
          }
        });
      } else {
        this.successCallback.push(() => {
          queueMicrotask(() => {
            try {
              let res = onFulfilled(this.value);
              // 拿到我们生成的实例,和用户的实例做对比
              resolveRes(res, p, resolve, reject);
            } catch (error) {
              reject(error.message);
            }
          });
        });
        this.failCallback.push(() => {
          queueMicrotask(() => {
            try {
              let res = onRejected(this.reason);
              resolveRes(res, p, resolve, reject);
            } catch (error) {
              reject(error.message);
            }
          });
        });
      }
    });
    return p;
}

编写 catch 实现

class MyPromise {
  // 。。。上面代码省略
  catch(e) {
    return this.then(null, e);
  }
}

测试实例

let p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject("抛出错误");
  }, 1000);
})
  .then(
    (val) => {
      console.log("success:", val);
    },
    (e) => {
      console.log("出错了", e);
      throw new Error("....");
    }
  )
  .catch((e) => {
    console.log("尾部捕获错误:", e);
  });

测试截图

在这里插入图片描述

2. resolve 静态方法实现

  • resolve 是 Promise 构造函数上的一个静态方法,可以通过 Promise.resolve(value) 来调用
  • resolve 方法返回一个新的 Promise 对象,该对象立即进入已解析状态,而不是等待异步操作完成
  • 如果传递给 resolve 的参数是一个 Promise 实例,返回的 Promise 将跟随该实例的最终状态(解析或拒绝)。
  • 如果参数是一个 thenable 对象(即具有 then 方法的对象),返回的 Promise 将等待 thenable 被解析或拒绝,并相应地解析或拒绝。

代码示例

  static resolve(val){
    // 1. 普通值就直接返回
    // 2. Promise 需要直接运行,需要放在构造函数中
    return val instanceof Promise ? val : new Promise((s) => s(val));
  }

测试实例

<script>
    MyPromise.resolve(123).then((val) => {
        console.log("resolve:", val);
      });
      let p = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve("1s 后输出");
        }, 1000);
      })

      MyPromise.resolve(p).then((val) => {
        console.log("resolve:", val);
      });
</script>

测试截图

在这里插入图片描述

3. finally(onFinally) 实例方法实现

  • finally 是一个实例方法,无论 Promise 最终是解析还是拒绝,都会执行 onFinally 回调函数。
  • finally 返回一个新的 Promise 对象,解析或拒绝的结果与原 Promise 相同。
  • finally 方法常用于清理工作,如关闭数据库连接、释放资源等。

代码示例

 finally(callback) {
    // 1. callback 需要执行
    // 2. 需要保证 原先的promise状态
    return this.then(
      (value) => {
        return Promise.resolve(callback()).then(() => value);
      },
      (reason) => {
        return Promise.resolve(callback()).then(() => {
          throw reason;
        });
      }
    );
  }

测试实例

<script>
    let p = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          reject("抛出错误");
        }, 1000);
      })
        .finally((e) => {
          console.log("finally执行了");
        })
        .then(null, (msg) => {
          console.log("finally后面的then : ", msg);
        });
</script>

测试截图

在这里插入图片描述

4. all 方法实现

  • all 是一个静态方法,它接受一个 Promise 数组作为参数。
  • 只有当数组中的所有 Promise 都成功解析时,all 返回的 Promise 才会解析,并且解析值为一个数组,包含所有 Promise 的解析值。
  • 如果数组中的任何一个 Promise 失败,all 返回的 Promise 将立即拒绝,拒绝原因为第一个失败的 Promise 的拒绝原因。

代码示例

static all(data) {
    // 1. 参数判定
    return new Promise((resolve, reject) => {
      if (Array.isArray(data)) {
        let res = [];
        let resIndex = 0;
        // 空数组 就直接返回
        if(data.length === 0){
          resolve(res)
          return
        }
        const addRes = (val, i) => {
          res[i] = val;
          resIndex++;
          // 这里不能使用 res.length, 切记 因为: arr[10] , arr.length ===10
          if (resIndex === data.length) {
            resolve(res);
          }
        };
        for (let index = 0; index < data.length; index++) {
          const item = data[index];
          if (item instanceof MyPromise) {
            item.then(
              (val) => {
                // 拿到值再增加
                addRes(val, index);
              },
              (msg) => {
                reject(msg);
              }
            );
          } else {
            addRes(item, index);
          }
        }
      } else {
        reject("all 参数必须是数组");
      }
    });
  }

测试实例

let p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(200);
    //reject("出错了。。。");
  }, 2000);
});
MyPromise.all([100, p1, 300]).then(
  (res) => {
    console.log("2s 后 all 执行结果:", res);
  },
  (msg) => {
    console.log("出错了:", msg);
  }
);

成功后的截图

在这里插入图片描述

出错后的截图

在这里插入图片描述

5. race 方法实现

  • Promise.race(iterable) 同样接受一个可迭代对象。
  • race 返回的 Promise 将根据可迭代对象中第一个解析或拒绝的 Promise 来确定自己的状态。
  • 如果数组中任何一个 Promise 解析,race 返回的 Promise 将解析,并以该 Promise 的解析值作为自己的解析值。
  • 如果任何一个 Promise 拒绝,race 返回的 Promise 将拒绝,并以该 Promise 的拒绝原因作为自己的拒绝原因。

代码示例

  static race(data) {
    return new MyPromise((resolve, reject) => {
      if (Array.isArray(data)) {
        // 空数组 就直接返回
        if (data.length === 0) {
          resolve();
          return;
        }
        for (let index = 0; index < data.length; index++) {
          const item = data[index];
          if (item instanceof MyPromise) {
            // 获取值,先得到的值就直接返回
            MyPromise.resolve(item).then(
              (val) => resolve(val),
              (msg) => reject(msg)
            );
          }
        }
      } else {
        reject("race 参数必须是数组");
      }
    });
  }

测试实例

let p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(200);
  }, 2000);
});
let p2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(300);
  }, 3000);
});
MyPromise.race([p2, p1]).then(
  (res) => {
    console.log("race 执行结果:", res);
  },
  (msg) => {
    console.log("race 出错了:", msg);
  }
);

测试截图

在这里插入图片描述

6. allSettled 方法实现

  • Promise.allSettled(iterable) 是 ES2020 新增的方法。
  • all 不同,allSettled 会等待可迭代对象中的所有 Promise 都完成(无论是解析还是拒绝)。
  • allSettled 返回的 Promise 解析后,结果是一个数组,每个元素是一个对象,包含 status(值为 "fulfilled""rejected")和 valuereason
  • 这个数组的顺序与输入的可迭代对象一致,无论 Promise 是解析还是拒绝。

代码示例

  static allSettled(data) {
    // 1. 参数判定
    return new Promise((resolve, reject) => {
      if (Array.isArray(data)) {
        let res = [];
        let resIndex = 0;
        // 空数组 就直接返回
        if (data.length === 0) {
          resolve(res);
          return;
        }
        for (let index = 0; index < data.length; index++) {
          const item = data[index];
          MyPromise.resolve(item).then(
            (value) => {
              res[index] = { status: "fulfilled", value };
              resIndex++;
              if (resIndex === data.length) resolve(res);
            },
            (reason) => {
              res[index] = { status: "rejected", reason };
              resIndex++;
              if (resIndex === data.length) resolve(res);
            }
          );
        }
      } else {
        reject("allSettled 参数必须是数组");
      }
    });
  }

测试实例

let p1 = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          resolve(200);
        }, 2000);
      });
      let p2 = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          reject(300);
        }, 3000);
      });
      MyPromise.allSettled([p2, p1]).then(
        (res) => {
          console.log("allSettled 执行结果:", res);
        },
        (msg) => {
          console.log("allSettled 出错了:", msg);
        }
      );

在这里插入图片描述

7. 全部实例代码


// 初始三个变量
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
  // 初始化参数
  state = PENDING;
  value = null;
  reason = "";
  // 构造函数
  constructor(exec) {
    // 传递两个函数,绑定 this 避免 this指向出错
    try {
      exec(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      console.log("exec。出错");
      this.reject(error.message);
    }
  }
  resolve(value) {
    // 判断状态, 修改状态 并赋值
    if (this.state === PENDING) {
      this.state = FULFILLED;
      this.value = value;
      // 这里遵循,先进先出,shift最前面的函数, 然后执行它
      while (this.successCallback.length) {
        this.successCallback.shift()();
      }
    }
  }
  reject(reason) {
    // 判断状态, 修改状态 并赋值
    if (this.state === PENDING) {
      this.state = REJECTED;
      this.reason = reason;
      while (this.failCallback.length) {
        this.failCallback.shift()();
      }
    }
  }
  successCallback = [];
  failCallback = [];
  then(onFulfilled, onRejected) {
    onFulfilled = onFulfilled ? onFulfilled : (value) => value;
    onRejected = onRejected
      ? onRejected
      : (reason) => {
          throw Error(reason);
        };
    // 返回一个新的实例
    let p = new Promise((resolve, reject) => {
      if (this.state === FULFILLED) {
        queueMicrotask(() => {
          try {
            let res = onFulfilled(this.value);
            resolveRes(res, p, resolve, reject);
          } catch (error) {
            reject(error.message);
          }
        });
      } else if (this.state === REJECTED) {
        queueMicrotask(() => {
          try {
            let res = onRejected(this.reason);
            resolveRes(res, p, resolve, reject);
          } catch (error) {
            reject(error.message);
          }
        });
      } else {
        this.successCallback.push(() => {
          queueMicrotask(() => {
            try {
              let res = onFulfilled(this.value);
              // 拿到我们生成的实例,和用户的实例做对比
              resolveRes(res, p, resolve, reject);
            } catch (error) {
              reject(error.message);
            }
          });
        });
        this.failCallback.push(() => {
          queueMicrotask(() => {
            try {
              let res = onRejected(this.reason);
              resolveRes(res, p, resolve, reject);
            } catch (error) {
              reject(error.message);
            }
          });
        });
      }
    });
    return p;
  }
  catch(e) {
    return this.then(null, e);
  }
  static resolve(val) {
    // 1. 普通值就直接返回
    // 2. Promise 需要直接运行,需要放在构造函数中
    return val instanceof Promise ? val : new Promise((s) => s(val));
  }
  finally(callback) {
    // 1. callback 需要执行
    // 2. 需要保证 原先的promise状态
    return this.then(
      (value) => {
        return Promise.resolve(callback()).then(() => value);
      },
      (reason) => {
        return Promise.resolve(callback()).then(() => {
          throw reason;
        });
      }
    );
  }

  static all(data) {
    // 1. 参数判定
    return new Promise((resolve, reject) => {
      if (Array.isArray(data)) {
        let res = [];
        let resIndex = 0;
        // 空数组 就直接返回
        if (data.length === 0) {
          resolve(res);
          return;
        }
        const addRes = (val, i) => {
          res[i] = val;
          resIndex++;
          // 这里不能使用 res.length, 切记 因为: arr[10] , arr.length ===10
          if (resIndex === data.length) {
            resolve(res);
          }
        };
        for (let index = 0; index < data.length; index++) {
          const item = data[index];
          if (item instanceof MyPromise) {
            item.then(
              (val) => {
                // 拿到值再增加
                addRes(val, index);
              },
              (msg) => {
                reject(msg);
              }
            );
          } else {
            addRes(item, index);
          }
        }
      } else {
        reject("all 参数必须是数组");
      }
    });
  }

  static race(data) {
    return new MyPromise((resolve, reject) => {
      if (Array.isArray(data)) {
        // 空数组 就直接返回
        if (data.length === 0) {
          resolve();
          return;
        }
        for (let index = 0; index < data.length; index++) {
          const item = data[index];
          if (item instanceof MyPromise) {
            MyPromise.resolve(item).then(
              (val) => resolve(val),
              (msg) => reject(msg)
            );
          }
        }
      } else {
        reject("race 参数必须是数组");
      }
    });
  }
  static allSettled(data) {
    // 1. 参数判定
    return new Promise((resolve, reject) => {
      if (Array.isArray(data)) {
        let res = [];
        let resIndex = 0;
        // 空数组 就直接返回
        if (data.length === 0) {
          resolve(res);
          return;
        }
        for (let index = 0; index < data.length; index++) {
          const item = data[index];
          MyPromise.resolve(item).then(
            (value) => {
              res[index] = { status: "fulfilled", value };
              resIndex++;
              if (resIndex === data.length) resolve(res);
            },
            (reason) => {
              res[index] = { status: "rejected", reason };
              resIndex++;
              if (resIndex === data.length) resolve(res);
            }
          );
        }
      } else {
        reject("allSettled 参数必须是数组");
      }
    });
  }
}
function resolveRes(res, promise, resolve, reject) {
  if (promise === res) {
    reject(new Error("不能是相同实例"));
    return;
  }
  if (res instanceof Promise) {
    res.then(resolve, reject); // 如果是promise,就调用它的then方法,并把当前的resolve,reject传递下去,它结束时就能调用
  } else {
    resolve(res);
  }
}

总结


通过本系列文章的深入探索,我们从零开始,一步步构建了对 Promise 机制的理解,实现了一个基本的 Promise 构造函数,并逐步扩展了它的功能,包括 thencatchallraceallSettled 等高级 API。这个过程不仅加深了我们对异步编程模式的认识,也锻炼了我们解决实际编程问题的能力。

手写 Promise 的旅程是一次对 JavaScript 核心概念的深刻反思。它教会我们,即使是日常使用的简单工具,背后也可能隐藏着复杂的逻辑和精巧的设计。在未来的编程道路上,无论是面对现有的库和框架,还是设计自己的解决方案,这种深入理解都将是我们宝贵的财富。


  • 31
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子羽bro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值