「JS硬气功👊」Promise异步移形换影——中(Promise链式调用的实现)

Hi! 这里是JustHappy ,既然点开了这个JS硬气功,那就一起愉快的修炼吧!我想大家和我一样,在面试中遇到Primise手写题 这类的总是想不起来,所以这次我们尝试使用JS硬气功暴打一下,话不多说,我们开始吧。

这篇我们再续上文,去实现一下Promise的 .then()/.catch()(包含链式调用)

我们的构造函数

我们在上篇所写的构造函数如下,我们将在这个基础上进行改进

如果需要看之前的实现逻辑,可以点这里「JS硬气功👊」Promise异步移形换影------上(我们一起暴打Promise手写题)

js 复制代码
class MyPromise {
  state = "pending";
  value = undefined;
  reason = undefined;
  resolveCallbacks = [];
  rejectCallbacks = [];
  constructor(fn) {
    const resolveHandler = (value) => {
      if (this.state === "pending") {
        this.state = "fulfilled";
        this.value = value;
        // 遍历执行成功的回调
        this.resolveCallbacks.forEach((fn) => fn(this.value));
      }
    };
    const rejectHandler = (reason) => {
      if (this.state === "pending") {
        this.state = "rejected";
        this.reason = reason;
        // 遍历执行失败的回调
        this.rejectCallbacks.forEach((fn) => fn(this.reason));
      }
    };
    try {
      fn(resolveHandler, rejectHandler);
    } catch {
      rejectHandler();
    }
  }
  // 这边以下是then
  then(fn1, fn2) {}
}

链式调用的实现

链式调用无非就是完善我们的 .then().catch() 方法,如果你对 .catch 方法比较熟悉,你会发现 .catch 方法实际上就是 .then 的一个语法糖,一个只返回错误的 .then,于是乎我们可以这样写

js 复制代码
class MyPromise {
  state = "pending";
  value = undefined;
  reason = undefined;
  resolveCallbacks = [];
  rejectCallbacks = [];
  constructor(fn) {
        ......
  }
  // 这边以下是then
  then(fn1, fn2) {}
  catch(fn){
      return this.then(null, fn)
  }
}

可以看到我们在 .catch 方法中给 .then 传入了 null,所以我们需要在 .then 方法中添加判断,这里我们使用三元实现

js 复制代码
class MyPromise {
  state = "pending";
  value = undefined;
  reason = undefined;
  resolveCallbacks = [];
  rejectCallbacks = [];
  constructor(fn) {
        ......
  }
  // 这边以下是then
  then(fn1, fn2) {
      fn1 = typeof fn1 === 'function' ? fn1 : (v) => v //不为function的时候参数直接返回
      fn2 = typeof fn2 === 'function' ? fn2 : (e) => e //不为function的时候参数直接返回
  }
  catch(fn){
      return this.then(null, fn)
  }
}

深入.then

我们回忆一下上篇, .then() 中要做什么呢?是的,如果在pending状态下,应该把传入的函数放到resolveCallbacksrejectCallbacks 中,其他状态下只要执行对应的函数和传递参数就行了,但是都是返回一个新的Promise对象,那么就会像下面这样

js 复制代码
class MyPromise {
  state = 'pending';
  value = undefined;
  reason = undefined;
  resolveCallbacks = [];
  rejectCallbacks = [];
  constructor(fn) {
        ......
  }
  // 这边以下是then
  then(fn1, fn2) {
      fn1 = typeof fn1 === 'function' ? fn1 : (v) => v 
      fn2 = typeof fn2 === 'function' ? fn2 : (e) => e 
      if(this.state === 'pending'){
          const p = new MyPromise((resolve, reject) => {})
          return p
      }
      if(this.state === 'fulfilled'){
          const p = new MyPromise((resolve, reject) => {})
          return p
      }
      if(this.state === 'rejected'){
          const p = new MyPromise((resolve, reject) => {})
          return p
      }
  }
  catch(fn){
      return this.then(null, fn)
  }
}

fulfilledrejected 的状态下相对来说比较好写,我们先来实现一下,很简单,就是执行对应的函数和传递参数,不过我们需要加个 try / catch 去验证一下传入的函数是否ok,那么就会是下面这样

js 复制代码
class MyPromise {
  state = 'pending';
  value = undefined;
  reason = undefined;
  resolveCallbacks = [];
  rejectCallbacks = [];
  constructor(fn) {
        ......
  }
  // 这边以下是then
  then(fn1, fn2) {
      fn1 = typeof fn1 === 'function' ? fn1 : (v) => v 
      fn2 = typeof fn2 === 'function' ? fn2 : (e) => e 
      if(this.state === 'pending'){
          const p = new MyPromise((resolve, reject) => {})
          return p
      }
      if(this.state === 'fulfilled'){ // 执行函数fn1 传递参数 value
          const p = new MyPromise((resolve, reject) => {
              try{
                  const newValue = fn1(this.value) // MyPromise的value
                  resolve(newValue)
              }catch(err){
                  reject(err)
              }
          })
          return p
      }
      if(this.state === 'rejected'){ // 执行函数fn2 传递错误原因 reason
          const p = new MyPromise((resolve, reject) => {
              try{
                  const newReason = fn2(this.reason) // MyPromise的value
                  reject(reason)
              }catch(err){
                  reject(err)
              }
          })
          return p
      }
  }
  catch(fn){
      return this.then(null, fn)
  }
}

ok,我们现在来处理pending 状态下的情况,也就是我们在未知之后状态的情况下我们需要存储任务,就是将传入的fn1、fn2存到resolveCallbacksrejectCallbacks,同样,我们不知道fn1、fn2是否是ok的,所以我们也需要使用try / catch去测试一下

js 复制代码
class MyPromise {
  state = 'pending';
  value = undefined;
  reason = undefined;
  resolveCallbacks = [];
  rejectCallbacks = [];
  constructor(fn) {
        ......
  }
  // 这边以下是then
  then(fn1, fn2) {
      fn1 = typeof fn1 === 'function' ? fn1 : (v) => v 
      fn2 = typeof fn2 === 'function' ? fn2 : (e) => e 
      if(this.state === 'pending'){
      // 注意,这里只是存储函数,并没有在这里执行
          const p = new MyPromise((resolve, reject) => {
              this.resolveCallbacks.push(() => {
                  try{
                      const newValue = fn1(this.value)
                      resolve(newValue)
                  }catch(err){
                      reject(err)
                  }
              })
              this.rejectCallbacks.push(() => {
                  try{
                      const newReason = fn2(this.reason)
                      reject(newReason)
                  }catch(err){
                      reject(err)
                  }
              })
          })
          return p
      }
      if(this.state === 'fulfilled'){
          ......
      }
      if(this.state === 'rejected'){ 
          ......
      }
  }
  catch(fn){
      return this.then(null, fn)
  }
}

ok,目前链式调用部分基本完成了,目前完整的代码如下

js 复制代码
class MyPromise {
  state = "pending";
  value = undefined;
  reason = undefined;
  resolveCallbacks = [];
  rejectCallbacks = [];
  constructor(fn) {
    const resolveHandler = (value) => {
      if (this.state === "pending") {
        this.state = "fulfilled";
        this.value = value;
        // 遍历执行成功的回调
        this.resolveCallbacks.forEach((fn) => fn(this.value));
      }
    };
    const rejectHandler = (reason) => {
      if (this.state === "pending") {
        this.state = "rejected";
        this.reason = reason;
        // 遍历执行失败的回调
        this.rejectCallbacks.forEach((fn) => fn(this.reason));
      }
    };
    try {
      fn(resolveHandler, rejectHandler);
    } catch {
      rejectHandler();
    }
  }
  // 这边以下是then
  then(fn1, fn2) {
    fn1 = typeof fn1 === "function" ? fn1 : (v) => v;
    fn2 = typeof fn2 === "function" ? fn2 : (e) => e;
    if (this.state === "pending") {
      const p = new MyPromise((resolve, reject) => {
        this.resolveCallbacks.push(() => {
          try {
            const newValue = fn1(this.value);
            resolve(newValue);
          } catch (err) {
            reject(err);
          }
        });
        this.rejectCallbacks.push(() => {
          try {
            const newReason = fn2(this.reason);
            reject(newReason);
          } catch (err) {
            reject(err);
          }
        });
      });
      return p;
    }
    if (this.state === "fulfilled") {
      // 执行函数fn1 传递参数 value
      const p = new MyPromise((resolve, reject) => {
        try {
          const newValue = fn1(this.value); // MyPromise的value
          resolve(newValue);
        } catch (err) {
          reject(err);
        }
      });
      return p;
    }
    if (this.state === "rejected") {
      // 执行函数fn2 传递错误原因 reason
      const p = new MyPromise((resolve, reject) => {
        try {
          const newReason = fn2(this.reason); // MyPromise的value
          reject(newReason);
        } catch (err) {
          reject(err);
        }
      });
      return p;
    }
  }
  catch(fn) {
    return this.then(null, fn);
  }
}

我们来测试一下吧!

这里的执行结果如下

如果是reject,就是下面这样

结果是下面这样

ok,我们链式调用的实现就到这里了,下篇我们继续完善我们的MyPromise

相关推荐
uhakadotcom7 分钟前
Apache Airflow入门指南:数据管道的强大工具
算法·面试·github
树上有只程序猿19 分钟前
后端思维之高并发处理方案
前端
uhakadotcom20 分钟前
Ruff:Python 代码分析工具的新选择
后端·面试·github
uhakadotcom23 分钟前
Mypy入门:Python静态类型检查工具
后端·面试·github
庸俗今天不摸鱼1 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
QTX187301 小时前
JavaScript 中的原型链与继承
开发语言·javascript·原型模式
黄毛火烧雪下1 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox1 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞1 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行1 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox