前端面试-浅析如何手写promise

260 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

如今前端面试中,除了问基础知识之外,经常还会问一些开放问题,来看开发者解决问题的思路,以及逻辑。

比如面试经常问的一个问题就是:如果让你手写一个promsie,你会怎么做?

像这种题目,面试官并不是真的想让你手写一个框架,而是看对promise的了解,思维方式

今天就简单的拆分一下手写promise的几个阶段

阶段1:

promsie大家肯定都用过,基本的用法也都知道。

new Promise((resolve, reject) => {
    resolve(1)
}).then(res => {
    console.log(res)
})

所以我们可以对promise进行一个简单的分析,至少可以得出三个结论

1. Promise是一个类

2. promise接收一个参数,参数是函数类型,且这个参数又接收两个函数参数,resolve, reject

3. promsie有一个then方法,一个catch方法

针对这三个结论,我们其实就可以开始写我们的第一个版本。

class MyPromise {
  constructor(fn) {
    this.status = "pending";
    this.resolve_value = "";
    this.reject_value = "";
    this.resolve_fn = (val) => {
      if (this.status === "pending") {
        this.status = "success";
        this.resolve_value = val;
      }
    };

    this.reject_fn = (val) => {
      if (this.status === "pending") {
        this.status = "fail";
        this.reject_value = val;
      }
    };
    fn(this.resolve_fn, this.reject_fn);
  }
  then(reslove_then) {
    if (this.status === "success") {
      reslove_then(this.resolve_value);
    }
  }
  catch(reject_catch) {
    if (this.status === "fail") {
      reject_catch(his.reject_value);
    }
  }
}

new MyPromise((resolve, reject) => {
  resolve(1);
}).then((res) => {
  console.log(res);
});

代码比较简单,都是基于之前的三个分析的代码。


版本2:

实现了基础版本,我们很容易就想到,promise是可以链式调用的,比如下面这样

new Promise((resolve, reject) => {
  resolve(1);
}).then((res) => {
  console.log(res);
  return 2
}).then(res => {
    console.log(res)
});

那么如何实现呢?

我们把then函数中的返回变成一个新的Promise对象就可以了。

  then(reslove_then) {
    return new MyPromise((reslove, reject) => {
        if (this.status === "success") {
            const okResult = reslove_then(this.resolve_value);
            reslove(okResult);
          }
    }) 
  }

new MyPromise((resolve, reject) => {
  resolve(1);
}).then((res) => {
  console.log(res);
  return 2
}).then(res => {
    console.log(res)
});

我们先来理一下逻辑(便于后续理解异步)

  1. 创建了一个Promise类,并把相应的参数传进去
  2. 执行resolve把值保存到resolve_value中
  3. 调用类的then方法,并传入一个函数reslove_then
  4. 执行函数并把resolve_value中的值传给reslove_then

image.png


版本3:

最后我们再来实现promise的异步功能,也是最主要的一个功能。

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
}).then((res) => {
  console.log(res);
});

异步的难点在于,第2步是异步的,所以在第4步中执行reslove_then的时候,resolve_value还没有被赋值,而且status还是pending状态。

那怎么解决呢?

我们可以先定义一个变量,把函数先存起来,等resolve执行完之后再执行。

// 1.先定义一个asynResolveList用来存异步resolve方法
this.asynResolveList = [];

// 2.然后在then方法中添加对pending的判断
if (this.status === "pending") {
    this.asynResolveList.push(() => {
      const okResult = reslove_then(this.resolve_value);
      reslove(okResult);
    });
  }

// 3.在resolve_fn最后执行asynResolveList中的方法
this.asynResolveList.forEach((fn) => {
    fn();
  });

异步的问题解决了, 那么如何解决异步链式调用呢?

只需要对上诉方法做一点小改动即可。对返回的okResult进行一次判断,如果返回的结果是一个promise,则我们获取promise.then中的值再传回去。

          if (typeof okResult === "object" && "then" in okResult) {
            okResult.then((res) => {
              reslove(res);
            });
          } else {
            reslove(okResult);
          }

完整代码【因为之前是用ts写的,就没有写js版本,文章中用的代码片段也是基于ts改过来的】

type RESOLVE_FN = (val: any) => void;
type REJECT_FN = (val: any) => void;
type EX_FN = (reslove: RESOLVE_FN, reject: REJECT_FN) => void;

class MyPromise {
  private resolve_fn!: RESOLVE_FN;
  private reject_fn!: REJECT_FN;
  private status!: string;
  private resolve_value!: any;
  private reject_value!: any;
  private asynResolveList: (() => void)[] = [];
  private asynRejectList: (() => void)[] = [];
  constructor(fn: EX_FN) {
    this.status = "pending";
    this.resolve_fn = (val) => {
      if (this.status === "pending") {
        this.status = "success";
        this.resolve_value = val;
        this.asynResolveList.forEach((fn) => {
          fn();
        });
      }
    };
    this.reject_fn = (val) => {
      if (this.status === "pending") {
        console.log("this.reject_fn");
        this.status = "fail";
        this.reject_value = val;
        this.asynRejectList.forEach((fn) => {
          fn();
        });
      }
    };
    fn(this.resolve_fn, this.reject_fn);
  }
  then(reslove_then: RESOLVE_FN) {
    return new MyPromise((reslove, reject) => {
      if (this.status === "success") {
        const okResult = reslove_then(this.resolve_value);
        reslove(okResult);
      }
      if (this.status === "fail") {
        reject(this.reject_value);
      }
      // 则表示是异步的
      if (this.status === "pending") {
        this.asynResolveList.push(() => {
          const okResult: any = reslove_then(this.resolve_value);
          // okResult的返回结果如果是promise
          if (typeof okResult === "object" && "then" in okResult) {
            okResult.then((res) => {
              reslove(res);
            });
          } else {
            reslove(okResult);
          }
        });
      }
    });
  }
  catch(reject_catch: REJECT_FN) {
    return new MyPromise((reslove, reject) => {
      if (this.status === "fail") {
        const failResult = reject_catch(this.reject_value);
        reject(failResult);
      }

      if (this.status === "success") {
        reslove(this.resolve_value);
      }
      if (this.status === "pending") {
        this.asynRejectList.push(() => {
          const failResult: any = reject_catch(this.reject_value);
          console.log("this", failResult);
          if (typeof failResult === "object" && "then" in failResult) {
            failResult.catch((error) => {
              reject(error);
            });
          } else {
            reject(failResult);
          }
        });
      }
    });
  }
}

感谢阅读,如有收获,别忘记点个赞哈!!!!