「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

相关推荐
灏瀚星空几秒前
用HTML5 Canvas打造交互式心形粒子动画:从基础到优化实战
前端·html·html5
Java技术小馆8 分钟前
打印高质量日志的10条军规
java·后端·面试
每次的天空8 分钟前
Android第十四次面试总结
android·面试·职场和发展
Jackson__14 分钟前
聊一下HTTP 与 HTTPS 的区别,以及HTTPS 的加密方式
前端·面试
一抓掉一大把27 分钟前
MiniExcel模板填充Excel导出
开发语言·javascript·ecmascript
好运yoo37 分钟前
npm install的原理
前端·npm
Jiaberrr42 分钟前
uniapp 安卓 APP 后台持续运行(保活)的尝试办法
android·前端·javascript·uni-app·app·保活
不老刘42 分钟前
uniapp+vue3实现CK通信协议(基于jjc-tcpTools)
前端·javascript·uni-app
蓝婷儿1 小时前
第二章支线八 ·CSS终式:Tailwind与原子风暴
前端·css
vanora11111 小时前
Vue在线预览excel、word、ppt等格式数据。
前端·javascript·vue.js