手写Promise[巨详细](未完)

Promise的一些知识和用法都讲了,此时就要来看看我们是否能够手写一个Promise构造函数出来,这样更有助于帮助我们理解Promise,在某些面试中也能过用得到。

初始结构搭建

这里我们借助构造函数的方法去搭建Promise的基础结构,因为Promise本身就是一个构造函数,当然也可以采用ES6里面的类去搭建。

js 复制代码
    function MyPromise() {}
    
    const p = new MyPromise((resolve, reject) => {});

接下来封装resolve函数和reject函数。我们在new Promise的时候会传一个执行函数 (resolve, reject) => {}, 我们就用形参executor去接收,因为Promise的执行函数会在new的过程中同步执行,所以这里我们也要去直接调用这个形参executor

js 复制代码
    function MyPromise(executor) {
      executor();
    }
    
    const p = new MyPromise((resolve, reject) => {});

执行器函数(resolve, reject) =>{}有两个参数,所以executor也得有两个参数executor(resolve,reject),但是这样会报错,因为resolvereject没有声明。

js 复制代码
    function MyPromise(executor) {
      executor(resolve, reject);
    }
    
    const p = new MyPromise((resolve, reject) => {});

所以接下来我们就要去声明这个resolve函数和reject函数,而且我们知道在调用这个resolve函数和reject函数的时候是可以传参的,所以我们还要用个形参去接收。

js 复制代码
    function MyPromise(executor) {
      function resolve(data) {}
      function reject(reason) {}
      executor(resolve, reject);
    }

    const p = new MyPromise((resolve, reject) => {});

这样基本的初始结构就搭建好了,接下来要去手写resolve函数和reject函数。

resolve函数和reject函数搭建

处理状态和结果

resolve函数会改变对象的状态和设置对象的结果值,所以首先先去添加对象状态属性和对象结果属性,状态初始为pending,结果初始为undefined,这都是前面讲的内容。

js 复制代码
    function MyPromise(executor) {
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      function resolve(data) {}
      function reject(reason) {}
      executor(resolve, reject);
    }

    const p = new MyPromise((resolve, reject) => {});

现在手写resolve函数的功能,去修改状态,然后给结果赋值。这里我们不能直接this.PromiseState = "fulfilled",看下面代码,resolve函数是直接调用的,这时候this指向的是全局,取不到PromiseState

js 复制代码
    let p = new Promise((resolve,reject) => {
      resolve()
    })

所以我们要在外面定义一个const self = this去保存这个this,然后self.PromiseState = "fulfilled",当然用bind方法也行。

js 复制代码
    function MyPromise(executor) {
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      // 保存this,方便后面resolve取值
      const self = this;
      function resolve(data) {
        self.PromiseState = "fulfilled";
        self.PromiseResult = data;
      }
      function reject(reason) {}
      executor(resolve, reject);
    }

    const p = new MyPromise((resolve, reject) => {});

reject函数同理去操作。

js 复制代码
    function MyPromise(executor) {
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      const self = this;

      function resolve(data) {
        self.PromiseState = "fulfilled";
        self.PromiseResult = data;
      }

      function reject(reason) {
        self.PromiseState = "rejected";
        self.PromiseResult = reason;
      }
      executor(resolve, reject);
    }

    const p = new MyPromise((resolve, reject) => {});

处理异常

除了上述修改状态和结果,我们还要考虑到抛出异常的操作,要处理异常就要用到try...catch,现在主要是想一下应该加在哪里。看下面代码:

js 复制代码
    let p = new Promise((resolve, reject) => {
      throw "异常";
    });

从上面可以得知抛出异常的代码是在执行器函数里执行的,所以我们应该把try...catch包裹在executor(resolve, reject)上,抛出异常以后状态就变为rejected,结果值就为抛出的异常,所以在catch里去调用reject函数,并且把抛出的异常传给reject函数。

js 复制代码
     function MyPromise(executor) {
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      const self = this;

      function resolve(data) {
        self.PromiseState = "fulfilled";
        self.PromiseResult = data;
      }

      function reject(reason) {
        self.PromiseState = "rejected";
        self.PromiseResult = reason;
      }
      
      // 在executor(resolve, reject)外包裹一层try...catch
      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }

    const p = new MyPromise((resolve, reject) => {});

设置状态只能改变一次

我们知道Promise实例对象的状态只能修改一次,所以我们需要再resolve函数和reject函数里面加一个判断,如果状态不是pending,就不进行操作。

js 复制代码
    function MyPromise(executor) {
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      const self = this;

      function resolve(data) {
        // 添加判断状态只能修改一次
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "fulfilled";
        self.PromiseResult = data;
      }

      function reject(reason) {
        // 添加判断状态只能修改一次
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "rejected";
        self.PromiseResult = reason;
      }

      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }

    const p = new MyPromise((resolve, reject) => {});

至此,resolve函数和reject函数的搭建基本就完成了。

then方法的搭建

给MyPromise的原型上添加静态方法then方法,then方法有两个函数参数onResolvedonRejected

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {};

then方法里面有两个函数参数,所以我们需要一个判断去确定应该执行哪一个函数。而这个判断就是判断p的状态是fulfilled还是rejected,因为then方法是由p调用的,所以p.then里的this指向的就是p,我们就可以通过this.PromiseState去判断。

而且调用then方法的参数函数的时候,这个参数函数也是有一个参数的,这个参数就是实例对象的PromiseResult,我们也要传过去。

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      if (this.PromiseState === "fulfilled") {
        onResolved(this.PromiseResult);
      }
      if (this.PromiseState === "rejected") {
        onRejected(this.PromiseResult);
      }
    };

我们现在来试一下基本的功能能不能实现:

js 复制代码
    function MyPromise(executor) {
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      const self = this;

      function resolve(data) {
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "fulfilled";
        self.PromiseResult = data;
      }

      function reject(reason) {
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "rejected";
        self.PromiseResult = reason;
      }

      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }

    MyPromise.prototype.then = function (onResolved, onRejected) {
      if (this.PromiseState === "fulfilled") {
        onResolved(this.PromiseResult);
      }
      if (this.PromiseState === "rejected") {
        onRejected(this.PromiseResult);
      }
    };

    const p1 = new MyPromise((resolve, reject) => {
      resolve("ok");
    });

    p1.then((res) => {
      console.log(res);
    });

    const p2 = new MyPromise((resolve, reject) => {
      reject("no ok");
    });

    p2.then(
      (res) => {},
      (error) => {
        console.log(error);
      }
    );

由此可见,基本的功能是可以实现的了。

异步任务回调的执行

前面实现的是同步任务的操作,执行器函数里面的代码是同步任务,这个时候功能是可以实现的,但是如果执行的是异步任务,还能够正常实现吗?

js 复制代码
    const p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve("ok");
      }, 1000);
    });

    p.then((res) => {
      console.log(res);
    });

由结果来看,如果执行函数里面执行的是异步任务的话,功能无法实现。

这是因为上面代码执行函数里是异步操作,一秒钟以后才会执行resolve函数,所以JS按照顺序执行到then方法的时候PromiseState还是pending,而我们对then方法的搭建中,只有对PromiseStatefulfilled或者rejected的判断然后去执行对应的后续处理,不会对PromiseStatepending有操作,而当一秒钟以后状态改变了,JS也早就过了then方法的执行了,不会再回过头再去执行一次then方法,所以我们需要对PromiseStatepending加上一个判断。

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      if (this.PromiseState === "fulfilled") {
        onResolved(this.PromiseResult);
      }
      if (this.PromiseState === "rejected") {
        onRejected(this.PromiseResult);
      }
      // 添加对状态为pending的判断
      if (this.PromiseState === "pending") {
      }
    };

那这个判断里面该进行哪些操作呢?首先,我们需要知道执行函数里面执行的是异步任务还是同步任务,所以我们去改造一下Promise的构造函数,给其添加一个属性callback

js 复制代码
    function MyPromise(executor) {
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      // 添加一个callback属性
      this.callback = {};
      const self = this;

      function resolve(data) {
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "fulfilled";
        self.PromiseResult = data;
      }

      function reject(reason) {
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "rejected";
        self.PromiseResult = reason;
      }

      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }

    const p = new MyPromise((resolve, reject) => {});

然后对在then方法里的this.PromiseState === "pending"判断中给callback赋值,把then方法的两个函数参数赋值给callback

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      if (this.PromiseState === "fulfilled") {
        onResolved(this.PromiseResult);
      }
      if (this.PromiseState === "rejected") {
        onRejected(this.PromiseResult);
      }
      // 如果状态为pending,就把then方法的两个函数参数赋值给callback
      if (this.PromiseState === "pending") {
        this.callback = {
          onResolved,
          onRejected,
        };
      }
    };

这样我们就可以知道执行函数里面执行的是异步任务还是同步任务了,因为只要callback里有值,就说明执行到then方法的时候状态为pending,就说明任务是异步任务,如果没有值,就说明是同步任务。

我们就可以在调用完resolve函数或者reject函数的时候加一个判断,如果callback有值,就说明是异步任务,这个时候已经不会再去执行then方法了,但是前面在执行then方法状态为pending的时候,就已经把then方法的两个函数参数onResolvedonRejected赋值给callback了,所以我们可以直接调用callback里的onResolved函数或者onRejected函数,就跟调用then方法是一样的了。

js 复制代码
    function MyPromise(executor) {
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      this.callback = {};
      const self = this;

      function resolve(data) {
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "fulfilled";
        self.PromiseResult = data;
        // 执行完以后通过callback判断是否为异步任务,是的话直接调用callback里对应的函数
        if (self.callback.onResolved) {
          self.callback.onResolved(data);
        }
      }

      function reject(reason) {
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "rejected";
        self.PromiseResult = reason;
        // 执行完以后通过callback判断是否为异步任务,是的话直接调用callback里对应的函数
        if (self.callback.onRejected) {
          self.callback.onRejected(reason);
        }
      }

      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }

    const p = new MyPromise((resolve, reject) => {});

到这里关于异步任务执行的回调就基本上完成了。

指定多个任务

Promise经常会有指定多个回调函数的情况,我们这里分两种情况,一种是执行函数里执行的是同步任务,另外一种是执行函数里执行的是异步任务。

执行函数里执行的是同步任务的情况:

js 复制代码
    function MyPromise(executor) {
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      this.callback = {};
      const self = this;

      function resolve(data) {
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "fulfilled";
        self.PromiseResult = data;
        if (self.callback.onResolved) {
          self.callback.onResolved(data);
        }
      }

      function reject(reason) {
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "rejected";
        self.PromiseResult = reason;
        if (self.callback.onRejected) {
          self.callback.onRejected(reason);
        }
      }

      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }

    MyPromise.prototype.then = function (onResolved, onRejected) {
      if (this.PromiseState === "fulfilled") {
        onResolved(this.PromiseResult);
      }
      if (this.PromiseState === "rejected") {
        onRejected(this.PromiseResult);
      }
      if (this.PromiseState === "pending") {
        this.callback = {
          onResolved,
          onRejected,
        };
      }
    };

    const p = new MyPromise((resolve, reject) => {
      resolve("ok");
    });

    // 同步任务指定多个回调函数
    p.then((res) => {
      console.log(1);
    });
    p.then((res) => {
      console.log(2);
    });

可以看到p状态改变以后,then方法会按照顺序去一个一个执行,所以功能是可以实现的。

执行函数里执行的是异步任务的情况:

js 复制代码
    const p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve("ok");
      }, 1000);
    });

    p.then((res) => {
      console.log(1);
    });
    p.then((res) => {
      console.log(2);
    });

可以看到,当执行函数里的任务是异步任务的时候,调用多个回调函数,只会执行最后一个回调函数。

这是因为两个p.then都走了PromiseState === "pending"这个判断,第一个p.thencallback赋值了以后,第二个p.then也给callback赋值了,就直接把callback原本的值给覆盖了,所以就只执行了最后一个回调函数。

所以我们需要把这些回调函数都保存起来,那么callback就不能是一个对象,而是一个数组,所以将callback改成callbacksp.thenPromiseState === "pending"这个判断的时候直接给callbacks数组去push,当然resolve函数和reject函数也要做对应的修改。

js 复制代码
    function MyPromise(executor) {
      this.PromiseState = "pending";
      this.PromiseResult = undefined;
      // 将callback由对象改为callbacks数组
      this.callbacks = [];
      const self = this;

      function resolve(data) {
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "fulfilled";
        self.PromiseResult = data;
        // 对应的修改,callbacks遍历去调用所有的回调函数
        self.callbacks.forEach((item) => {
           // 判断有没有对应的后续处理
           item.onResolved && item.onResolved(data);
        });
      }

      function reject(reason) {
        if (self.PromiseState !== "pending") return;
        self.PromiseState = "rejected";
        self.PromiseResult = reason;
        // 对应的修改,callbacks遍历去调用所有的回调函数
        self.callbacks.forEach((item) => {
          // 判断有没有对应的后续处理
          item.onRejected && item.onRejected(reason);
        });
      }

      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }

    MyPromise.prototype.then = function (onResolved, onRejected) {
      if (this.PromiseState === "fulfilled") {
        onResolved(this.PromiseResult);
      }
      if (this.PromiseState === "rejected") {
        onRejected(this.PromiseResult);
      }
      if (this.PromiseState === "pending") {
        // 直接让callbacks数组去push,多个回调函数都放进callbacks里
        this.callbacks.push({
          onResolved,
          onRejected,
        });
      }
    };

    const p = new MyPromise((resolve, reject) => {});

此时再调用多个回调函数,功能就可以正常实现了。

js 复制代码
    const p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve("ok");
      }, 1000);
    });

    p.then((res) => {
      console.log(1);
    });
    p.then((res) => {
      console.log(2);
    });

指定多个回调函数的功能也基本实现。

同步任务修改then方法的返回值

Promise其中的链式调用很重要,而要想实现链式调用then方法必须得是返回一个Promise实例对象,现在我们就来完成这个功能。

then方法要返回一个Promise实例对象,我们就在then方法里去return一个Promise实例对象:

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      // 在原本的操作外面包裹一层 new MyPromise
      return new MyPromise((resolve, reject) => {
      
        // 这里是原本的操作 
        if (this.PromiseState === "fulfilled") {
          onResolved(this.PromiseResult);
        }
        if (this.PromiseState === "rejected") {
          onRejected(this.PromiseResult);
        }
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved,
            onRejected,
          });
        }
        
      }); 
    };

不过这里返回的Promise实例对象状态为pending,结果为undefined,还需要再处理一下。

then方法返回的三种情况

前几篇文章说了,then方法的返回结果有三种情况:

第一个是没有相关后续处理,后边的任务和前面保持一致。就是说p的状态改变以后,p.then方法没有对应的回调函数去处理,那么一来p.then返回的实例对象newP就跟p保持一致。

js 复制代码
    let p = new Promise((resolve, reject) => {
      resolve("123");
    });
    let newP = p.then(null, (res) => {});
    console.log(newP);

第二个是有相关后续处理,但是还没有执行,新任务挂起。就是说p有后续处理,但是p还没有执行结束,也就是状态还未改变,此时不会调用对应的回调函数,所以叫做还没有执行。此时的newP的状态就是pending,结果为undefined

js 复制代码
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject("123");
      }, 3000);
    });
    let newP = p.then(null, (res) => {});
    console.log(newP);

第三个就是有相关后续处理,并且已经执行,这个又会根据执行结果分别有好几种情况:

  • 回调函数抛出异常:newP的状态就是rejected,结果就是抛出去的异常。
  • 回调函数return一个非Promise实例对象:newP的状态就是fulfilled,结果就是return出去的值。
  • 回调函数不return,也不抛出异常:参考第二个,看成return出去一个undefinednewP的状态就是fulfilled,结果就是undefined
  • 回调函数return一个Promise实例对象:那么newP的状态和结果就和这个return出去的Promise实例对象相同。

第一种情况和第二种情暂时不用管,我们这里主要是考虑第三种情况,然后根据第三种情况的回调函数不同的执行结果去处理。

第三种情况其实主要就是分为两种,一种return的情况,一种是抛出异常的情况。

搭建return的情况

首先我们要获取回调函数的执行结果result,然后对其判断是否为Promise实例对象。

如果result为Promise实例对象,就要返回相同状态和结果的实例对象,我们可以在里面去调用then方法,如果result状态是成功的就调用resolve(v)v就是result的结果,就相当于return new MyPromise((resolve,reject) => {resolve(v)}),如果result状态是失败的就调用reject(r)r就是result的结果,就相当于return new MyPromise((resolve,reject) => {reject(r)}),这样一来就可以返回跟result相同状态和结果的实例对象了。

有人可能会说为什么不直接返回这个result呢,别忘了外面还有一层return new MyPromise((resolve, reject) => {})呢。如果跨出这一层在这个外面去判断又多少有点麻烦了。

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
    
      return new MyPromise((resolve, reject) => {
      
        if (this.PromiseState === "fulfilled") {
          // 拿到执行结果,这个result就是回调函数的返回值,如果没有返回值result就为undefined
          let result = onResolved(this.PromiseResult);
          // 对其判断是否为Promise实例对象
          if (result instanceof MyPromise) {
            // 如果是Promise实例对象,就说明回调函数执行结果是return一个实例对象,那么我们应该返回跟这个实例对象相同状态和结果的实例对象
            // 可以去调用then方法,如果回调函数执行结果return的是成功状态的实例对象
            // 就会执行resolve(v),这样就相当于 return new MyPromise((resolve,reject) => {resolve(v)}) v 就是result实例对象的结果,这样一来就返回result相同状态和结果的实例对象了
            // 如果回调函数执行结果return的是失败状态的实例对象,就调用reject(r)也是一样的
            // 注意不要被这个then方法迷住了,这个then方法不要去考虑我们外面嵌套的`return new MyPromise((resolve, reject) => {})`,只要去处理resolve(v)就好了,又不是取返回值,反正我是迷住了
            result.then(
              (v) => {
                resolve(v);
              },
              (r) => {
                reject(r);
              }
            );
          } else {
            // 如果不是Promise对象,then返回的Promise状态就是fulfilled,值就是result
            // 这样就相当于 return new MyPromise((resolve,reject) => {resolve(result)}) 返回的实例对象状态为fulfilled,结果为result
            resolve(result);
          }
        }
        
        // 这边也是一样
        if (this.PromiseState === "rejected") {
          let result = onRejected(this.PromiseResult);
          if (result instanceof MyPromise) {
            result.then(
              (v) => {
                resolve(v);
              },
              (r) => {
                reject(r);
              }
            );
          } else {
            resolve(result);
          }
        }
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved,
            onRejected,
          });
        }
        
      });
      
    };

搭建抛出异常的情况

讲完了return的情况,现在就来讲讲抛出异常的情况,抛出异常肯定是用try...catch去处理。跟我们讲搭建resolve函数的时候一样,现在要考虑这个该放在哪里。

看下面这段代码我们可以知道抛出异常的代码是执行在状态改变以后的后续操作,可能是状态成功执行onResolved函数的时候也可能是状态失败执行onRejected函数的时候。

js 复制代码
    // 状态成功
    p.then(
      () => {
        throw "抛出错误";
      },
      () => {}
    );

    // 状态失败
    p.then(
      () => {},
      () => {
        throw "抛出错误";
      }
    ); 

所以这个try...catch应该放在我们自己定义的then方法中判断完状态准备执行后续操作的时候。我们知道处理异常就是返回一个失败状态的Promise实例对象,结果就是抛出去的异常,所以直接在catch里调用reject函数。

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      return new MyPromise((resolve, reject) => {
        if (this.PromiseState === "fulfilled") {
          // 放在这里,刚判断完状态的地方,把这个if语句下的代码段都包裹住  
          try {
            let result = onResolved(this.PromiseResult);
            if (result instanceof MyPromise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            // 如果抛出异常,就是返回一个失败状态的Promise实例对象,结果就是抛出的异常,所以直接调用reject函数
            reject(e);
          }
        }
        if (this.PromiseState === "rejected") {
          // 这边也是一样
          try {
            let result = onRejected(this.PromiseResult);
            if (result instanceof MyPromise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        }
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved,
            onRejected,
          });
        }
      });
    };

异步任务修改then方法的返回值

前面我们搭建了同步任务的then方法的返回,但是这样的话是否适用于异步任务呢?我们来试试。

js 复制代码
    let p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve("ok");
      }, 1000);
    });
    const res = p.then((value) => {
      console.log(value);
    });
    console.log(res);

预想应该返回状态为fulfilled,结果为1的实例对象,但是这里返回的是状态为pending,结果为undefined,是因为这里走了then方法的this.PromiseState === "pending"判断。

js 复制代码
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved,
            onRejected,
          });
        }

这个判断里面的操作知识单纯调用了onResolved函数和onRejected函数,没有做类似同步任务搭建的那些操作,所以我们需要给加上,跟同步一样,现在也是只考虑return的情况和抛出异常的情况。

这里的操作的主要是this.callbacks.push()onResolved函数和onRejected函数

搭建return的情况

onResolved函数和onRejected函数进行和同步任务一样的修改。

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      return new MyPromise((resolve, reject) => {
        if (this.PromiseState === "fulfilled") {
          try {
            let result = onResolved(this.PromiseResult);
            if (result instanceof MyPromise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        }
        if (this.PromiseState === "rejected") {
          try {
            let result = onRejected(this.PromiseResult);
            if (result instanceof MyPromise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        }
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved: function () {
              // 在这里的操作和同步处理差不多,唯一的区别是这里是对象调用onResolved,所以不能用 this
              let result = onResolved(self.PromiseResult);
              // 然后进行判断
              if (result instanceof Promise) {
                result.then(
                  (v) => {
                    resolve(v);
                  },
                  (r) => {
                    reject(r);
                  }
                );
              } else {
                resolve(result);
              }
            },
            onRejected: function () {
              // 俺也一样
              let result = onRejected(self.PromiseResult);
              if (result instanceof Promise) {
                result.then(
                  (v) => {
                    resolve(v);
                  },
                  (r) => {
                    reject(r);
                  }
                );
              } else {
                resolve(result);
              }
            },
          });
        }
      });
    };

搭建抛出异常的情况

这里的搭建和前面处理异常的情况一样,也是用try...catch去处理,位置跟处理同步的时候放的一样。

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      return new MyPromise((resolve, reject) => {
        if (this.PromiseState === "fulfilled") {
          try {
            let result = onResolved(this.PromiseResult);
            if (result instanceof MyPromise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        }
        if (this.PromiseState === "rejected") {
          try {
            let result = onRejected(this.PromiseResult);
            if (result instanceof MyPromise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        }
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved: function () {
              // 跟同步一样,在改变状态执行后续操作的时候包裹
              try {
                let result = onResolved(self.PromiseResult);
                if (result instanceof MyPromise) {
                  result.then(
                    (v) => {
                      resolve(v);
                    },
                    (r) => {
                      reject(r);
                    }
                  );
                } else {
                  resolve(result);
                }
              } catch (e) {
                reject(e);
              }
            },
            onRejected: function () {
              // 俺也一样
              try {
                let result = onRejected(self.PromiseResult);
                if (result instanceof MyPromise) {
                  result.then(
                    (v) => {
                      resolve(v);
                    },
                    (r) => {
                      reject(r);
                    }
                  );
                } else {
                  resolve(result);
                }
              } catch (e) {
                reject(e);
              }
            },
          });
        }
      });
    };

then方法的完善

我们在前面讲搭建then方法的返回值的时候,会有一段代码反复用到,就是下面这段代码:

js 复制代码
try {
  let result = onResolved(this.PromiseResult);
  if (result instanceof MyPromise) {
    result.then(
      (v) => {
        resolve(v);
      },
      (r) => {
        reject(r);
      }
    );
  } else {
    resolve(result);
  }
} catch (e) {
  reject(e);
}

所以我们可以让他复用起来,创建一个callback函数存放复用代码:

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      // 老规矩,因为callback的this获取不到结果和状态,所以提前讲this赋值给self
      const self = this;
      return new MyPromise((resolve, reject) => {
      
        // type存放的是调用onResolved函数还是onRejected函数
        function callback(type) {
          try {
            let result = type(self.PromiseResult);
            if (result instanceof MyPromise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        }
        if (this.PromiseState === "fulfilled") {
          callback(onResolved);
        }
        if (this.PromiseState === "rejected") {
          callback(onRejected);
        }
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved: function () {
              callback(onResolved);
            },
            onRejected: function () {
              callback(onRejected);
            },
          });
        }
      });
    };

到此,then方法基本搭建完成。

搭建catch方法及异常穿透

搭建catch方法

搭建完then方法后现在要开始搭建catch方法了,不过我们已经搭建完then方法了,而catch方法其实就是调用then方法的第二个回调函数,所以我们可以直接取调用then方法:

js 复制代码
    MyPromise.prototype.catch = function (onRejected) {
      // this就是调用catch方法的Promise实例对象
      return this.then(undefined, onRejected);
    };

我们来试试能不能实现功能:

js 复制代码
    let p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        reject(123);
      }, 1000);
    });

    let result = p.catch((err) => {
      console.log(err);
      return 1;
    });
    console.log(result);

可以看到基本功能比如catch方法的调用和返回都是可以实现的。

搭建异常穿透

catch的基本功能实现以后现在就要搭建它的异常穿透功能了,还记得什么是异常穿透吧。Promise在链式调用的时候如果某一步状态失败了会去直接调用最后的catch方法。

js 复制代码
    p.then((res) => {})
      .then((res) => {})
      .then((res) => {})
      .catch((err) => {});

在讲搭建异常穿透之前我们先来看一下几个案例:

js 复制代码
    let p = new MyPromise((resolve, reject) => {
      resolve(123);
    });

    p.then((res) => {
      console.log(res);
    }).catch((err) => {
      console.log(err);
    });
js 复制代码
    let p = new MyPromise((resolve, reject) => {
      reject(123);
    });

    p.then((res) => {
      console.log(res);
    }).catch((err) => {
      console.log(err);
    });

为什么第一个成功,而第二个会打印报错呢,那是因为第一个是成功状态的Promise实例对象,调用then方法的时候会走到下面这一步,然后我们也可以在p.then((res) => {})中获取到这个onResolved函数。

js 复制代码
if (this.PromiseState === "fulfilled") {
  callback(onResolved);
}

而第二个是失败状态的Promise实例对象,调用then方法的时候会走到下面这一步,但是我们无法在p.then((res) => {})中获取到这个onRejected函数。

js 复制代码
if (this.PromiseState === "rejected") {
  callback(onRejected);
}

那么在调用callback函数的时候,传入的type就是undefined,然后走到let result = type(self.PromiseResult)就会有异常,因为undefined不能作为函数,所以会走catch (e) {reject(e)}去调用reject(e),此时打印e会发现是type is not a function,所以此时p.then((res) => {})就会返回一个状态为失败,结果为type is not a function的实例对象,然后再调用catch方法,就会打印type is not a function这个错误。

js 复制代码
function callback(type) {
  try {
    let result = type(self.PromiseResult);
    if (result instanceof MyPromise) {
      result.then(
        (v) => {
          resolve(v);
        },
        (r) => {
          reject(r);
        }
      );
    } else {
      resolve(result);
    }
  } catch (e) {
    reject(e);
  }
}

这里比较绕,不知道能不能理解,我在每一步做一个打印看一下,能理解就理解,理解不了也无所谓。

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      const self = this;
      return new MyPromise((resolve, reject) => {
        function callback(type) {
          console.log(2,'调用callback方法');
          try {
            console.log(3,'进入到callback方法的try...catch里');
            console.log(type,'传入的onRejected函数');
            let result = type(self.PromiseResult);
            console.log(result,'获取onRejected函数的结果,走到这一步是p.catch的,因为p.then前面type(self.PromiseResult)会出现异常直接去下面了走不到这一步,此时为undefined是因为p.catch没有有返回值');
            if (result instanceof MyPromise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            console.log(e,`进入try...catch异常以后调用reject函数,此时走完p.then()然后返回一个失败状态结果为${e}的实例对象,然后再调用p.catch方法`);
            reject(e);
          }
        }
        if (this.PromiseState === "fulfilled") {
          callback(onResolved);
        }
        if (this.PromiseState === "rejected") {
          console.log(1, '走到失败状态的判断,第一次走是因为p.then,第二次走是因为p.catch');
          callback(onRejected);
        }
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved: function () {
              callback(onResolved);
            },
            onRejected: function () {
              callback(onRejected);
            },
          });
        }
      });
    };

    MyPromise.prototype.catch = function (onRejected) {
      // this就是调用catch方法的Promise实例对象
      return this.then(undefined, onRejected);
    };

    let p = new MyPromise((resolve, reject) => {
      reject(123);
    });

    p.then((res) => {
      console.log(res);
    }).catch((err) => {
      console.log(err,'这里是p.catch的打印');
    });

所以我们要先对then方法进行改造,对是否有onRejected函数进行判断,如果不是函数(也不考虑是否未别的类型,不是函数一般情况下就是没传),就给它设置成函数。

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      const self = this;
      // 为了实现then方法可以只带一个参数函数,判断onRejected有没有传
      if (typeof onRejected !== "function") {
        // 如果不是函数,就要给onRejected一个初始值,而不是为 undefined
        onRejected = (reason) => {
          throw reason;
        };
      }
      return new MyPromise((resolve, reject) => {
        function callback(type) {
          try {
            let result = type(self.PromiseResult);
            if (result instanceof MyPromise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        }
        if (this.PromiseState === "fulfilled") {
          callback(onResolved);
        }
        if (this.PromiseState === "rejected") {
          callback(onRejected);
        }
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved: function () {
              callback(onResolved);
            },
            onRejected: function () {
              callback(onRejected);
            },
          });
        }
      });
    };

然后此时就可以实现功能了,具体分析情况可以看一下我上面怎么分析报错的步骤,然后自己想一想。

js 复制代码
    let p = new MyPromise((resolve, reject) => {
      reject(123);
    });

    p.then((res) => {
      console.log(res);
    }).catch((err) => {
      console.log(err);
    });

此时then方法不用传第二个函数参数的功能就实现了,那么我们是否可以让then方法连第一个函数参数都不传呢,正常的Promise是可以实现的,所以我们也要搭建一下。

也是一样的操作,判断onResolved函数是否存在,不存在就给个初始值。

js 复制代码
    MyPromise.prototype.then = function (onResolved, onRejected) {
      const self = this;
      if (typeof onRejected !== "function") {
        onRejected = (reason) => {
          throw reason;
        };
      }

      // 让then方法可以不传第一个函数参数
      if (typeof onResolved !== "function") {
        onResolved = (value) => value;
      }
      return new MyPromise((resolve, reject) => {
        function callback(type) {
          try {
            let result = type(self.PromiseResult);
            if (result instanceof MyPromise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        }
        if (this.PromiseState === "fulfilled") {
          callback(onResolved);
        }
        if (this.PromiseState === "rejected") {
          callback(onRejected);
        }
        if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved: function () {
              callback(onResolved);
            },
            onRejected: function () {
              callback(onRejected);
            },
          });
        }
      });
    };
    
    p.then().then((res) => {
      console.log(123);
    });

好了,现在异常穿透的功能也就正式搭建完了,还记得我们前面搭建then方法的返回值的时候说的,then方法返回有三种情况,第一个是没有相关后续处理,后边的任务和前面保持一致;第二个是有相关后续处理但是还未执行;第三个是有相关后续处理且已经执行。当时我们说只考虑第三种情况,现在第一种情况也在这里完成了,而第二种情况不就是前面在讲搭建then方法的返回的时候其中的异步任务的情况吗,其实也已经完成了。

现在Promise的搭建就基本上完成了。

搭建Promise.resolve()

我们讲resolve方法的时候,有以下几种情况:

Promise.resolve()

  • 返回一个成功/失败的Promise对象。

  • 接收一个参数,分为四种情况。

    • 如果传入一个非Promise类型对象,则返回为一个Promise实例对象,状态为成功,结果值就是传入的参数值;
    • 如果传入的是一个Promise类型的对象,会直接返回这个Promise类型的对象。
    • 如果传入的是一个thenable对象,thenable对象就是一个带有then方法的对象,resolve会将这个对象直接转化为Promise对象,然后执行thenable对象的then方法。
    • 如果不传入参数,会返回一个状态为成功的Promise实例对象,结果为undefined。

这个跟then方法的返回值搭建差不多,我就直接放代码了,如果不懂,再回去看看then方法的返回值搭建。

js 复制代码
    MyPromise.resolve = function (value) {
      // 返回一个 Promise 对象
      return new MyPromise((resolve, reject) => {
        // 判断传入的值
        if (value instanceof MyPromise) {
          // 是 Promise,调用 then()
          value.then(
            (v) => {
              resolve(v);
            },
            (r) => {
              reject(r);
            }
          );
        } else {
          // 不是 Promise
          // 状态设置为成功,结果就是传入的值,直接调用 resolve()
          resolve(value);
        }
      });
    };

试验一下:

js 复制代码
    const p = MyPromise.resolve("ok");
    const p2 = MyPromise.resolve(
      new MyPromise((resolve, reject) => {
        resolve("success");
      })
    );
    const p3 = MyPromise.resolve(
      new MyPromise((resolve, reject) => {
        reject("no ok");
      })
    );
    const p4 = MyPromise.resolve(
      new MyPromise((resolve, reject) => {
        setTimeout(() => {
          resolve("success");
        }, 1000);
      })
    );
    const p5 = MyPromise.resolve(
      new MyPromise((resolve, reject) => {
        new MyPromise((resolve, reject) => {
          setTimeout(() => {
            reject("no ok");
          }, 1000);
        });
      })
    );
    console.log(p, "p");
    console.log(p2, "p2");
    console.log(p3, "p3");
    console.log(p4, "p4");
    console.log(p5, "p5");  // 里面的是一个失败状态的Promise对象,但是外面没有调用resolve或者reject,所以状态为pending

搭建Promise.reject()方法

我们讲reject方法的时候,返回的是一个失败状态,结果值就是传进去值的Promise实例对象。

Promise.reject()

  • 无论参数是什么,都会返回一个失败的Promise对象,结果值就是参数本身。

这个就更简单了,直接调用reject函数就好。

js 复制代码
    MyPromise.reject = function (value) {
      // 返回一个 Promise 对象
      return new Promise((resolve, reject) => {
        reject(value);
      });
    };
相关推荐
irving同学462382 分钟前
Next.js 组件开发最佳实践文档(TypeScript 版)
前端
刺客-Andy7 分钟前
React Vue 项开发中组件封装原则及注意事项
前端·vue.js·react.js
marzdata_lily17 分钟前
从零到上线!7天搭建高并发体育比分网站全记录(附Java+Vue开源代码)
前端·后端
小君31 分钟前
让 Cursor 更加聪明
前端·人工智能·后端
顾林海40 分钟前
Flutter Dart 异常处理全面解析
android·前端·flutter
残轩1 小时前
JavaScript/TypeScript异步任务并发实用指南
前端·javascript·typescript
用户88442839014251 小时前
xterm + socket.io 实现 Web Terminal
前端
helloYaJing1 小时前
代码封装:超时重传方法
前端
literature16881 小时前
隐藏的git文件夹
前端·git
12码力1 小时前
使用 Three.js + Three-Tile 实现地球场景与几何体
前端