「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

相关推荐
anyup_前端梦工厂2 小时前
了解几个 HTML 标签属性,实现优化页面加载性能
前端·html
前端御书房2 小时前
前端PDF转图片技术调研实战指南:从踩坑到高可用方案的深度解析
前端·javascript
2301_789169542 小时前
angular中使用animation.css实现翻转展示卡片正反两面效果
前端·css·angular.js
风口上的猪20153 小时前
thingboard告警信息格式美化
java·服务器·前端
程序员黄同学3 小时前
请谈谈 Vue 中的响应式原理,如何实现?
前端·javascript·vue.js
张胤尘4 小时前
C/C++ | 每日一练 (2)
c语言·c++·面试
爱编程的小庄4 小时前
web网络安全:SQL 注入攻击
前端·sql·web安全
宁波阿成5 小时前
vue3里组件的v-model:value与v-model的区别
前端·javascript·vue.js
柯腾啊5 小时前
VSCode 中使用 Snippets 设置常用代码块
开发语言·前端·javascript·ide·vscode·编辑器·代码片段
Jay丶萧邦5 小时前
el-select:有关多选,options选项值不包含绑定值的回显问题
javascript·vue.js·elementui