ES6-Promise之手写Promise

264 阅读5分钟

相信大家对Promise都有基本的认识 尤其是在React Vue项目 Promise使用频率还是很高的,Promise的出生是为了解决一个事情-让开发者更合理、更规范地用于处理异步操作

因为js是单线程的设计,js有同步任务和异步任务两个队列,同步:顾名思义,就是按顺序依次执行,异步则是没有顺序的,会根据执行时长去返回任务的结果,常见于开发过程中的ajax请求,当我们使用一个请求的结果作为下个请求得我参数时,回调嵌套就会形成回调地狱:

   $ajax({
       success(res1){
            $ajax({
               success(res2){
                    $ajax({
                       success(res3){
                           console.log('回调地狱就形成了')
                       }
                   })
               }
           })
       }
   })

为了解决以上的类似问题 Promise就出生了 Promise A+规范 ,传统的写法,为了得到res3,我们必须依赖res1和res2,而且有更多层和更多的逻辑运算的时候,代码特别难以维护,也增加了请求时更多的等待时间,ES6为了规范它,所以就有了Promise的出现了。Promise对象能使我们更合理、更规范地进行处理异步操作。

Promise 的基本形式

    const prs = new Promise((resolve,reject)=>{
        resolve('成功的结果')
        reject('失败的结果')
    })

Promise的三种状态

1.pending:Promise被刚创建的时候状态为pending,表示初始状态;

2.fulfilled:resolve方法调用的时候,表示操作成功;

3.rejected:reject方法调用的时候,表示操作失败;

状态只能从初始化转换为成功或失败,状态不可逆

.then 和.catch方法

.then为处理成功后的业务逻辑

    prs.then(res=>{
        //res为成功的返回值 也就是我们通过resolve包裹的结果
    })

.catch为处理失败后的业务逻辑

    prs.then(res=>{
        //res为成功的返回值 也就是我们通过resolve包裹的结果
    }).catch(error=>{
        //error是失败的结果 我们通过reject包裹的结果
    })

Promise.all()方法,接受一个数组作为参数,数组的元素是Promise实例对象,当参数中的实例对象的状态都为fulfilled时,Promise.all( )才会有返回。


    const prs1 = new Promise((resolve,reject)=>{
        resolve('prs1成功执行了')
    })
    const prs2 = new Promise((resolve,reject)=>{
        resolve('prs2成功执行了')
    })
    Promise.all([prs1,prs2]).then(res=>{
        此时prs1和prs2都执行成功Promise.all才会返回正确的数组,有一个失败就会导致Promise.all()失败
    })

Promise.race()方法,接受一个数组作为参数,数组的元素是Promise实例对象,当参数中的实例对象的状态都有一个为fulfilled时,Promise.race( )就会返回正确的结果。


    const prs1 = new Promise((resolve,reject)=>{
        resolve('prs1成功执行了')
    })
    const prs2 = new Promise((resolve,reject)=>{
        reject('prs2执行失败了')
    })
    Promise.race([prs1,prs2]).then(res=>{
        此时prs1执行成功了,prs2执行失败了,但是Promise.race()会返回prs1作为执行成功的结果,因为Promise.race()有一个执行成功就会返回第一个成功对应的结果
    })

回顾完Promise的基础知识,下面开始手写Promise

    class Promise {
      PromiseState = "pending"; //准备状态
      PromiseResult = null; //结果
      FULFILLED = "fulfilled"; //成功的状态
      REJECTED = "rejected"; //成功的状态
      fulfilledList = []; //成功回调的管理数组
      rejectedList = []; //失败回调的管理数组
      constructor(executor) {
        this.initBind();
        try {
          executor(this.resolve, this.reject);
        } catch (error) {
          this.reject(error);
        }
      }
      initBind() {
        this.resolve = this.resolve.bind(this);
        this.reject = this.reject.bind(this);
      }
      // 成功的执行函数
      resolve = (value) => {
        this.PromiseState = this.FULFILLED; //改变状态
        this.PromiseResult = value; //赋值
      };
      reject = (reason) => {
        this.PromiseState = this.REJECTED; //改变状态
        this.PromiseResult = reason; //赋值
      };
      // then方法可以接受两个回调 分别是成功和失败的回调
      then(onFulfilled, onRejected) {
        onFulfilled =
          typeof onFulfilled === "function" ? onFulfilled : (val) => val;
        onRejected =
          typeof onRejected === "function"
            ? onRejected
            : (reason) => {
                throw reason;
              };
        // 根据状态执行回调函数
        if (this.PromiseState === this.FULFILLED) onFulfilled(this.PromiseResult);
        if (this.PromiseState === this.REJECTED) onRejected(this.PromiseResult);
      }
    }
    
    
    
    //基础的then方法就写好了 先测试一下
    const prs = new Promise((resolve, reject) => {
      resolve("成功");
      // reject("失败");
    });
    prs.then(
      (res) => {
        console.log(res);
      },
      (error) => {
        console.log(error);
      }
    );
    //控制台分别执行了对应的结果 ok 继续完善then方法

image.png

这个时候我们的then方法是同步的 并且我们的结果只判断了状态为FULFILLED和REJECTED,如果在new Promise中执行一个异步时 就无法执行

    const prs = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("成功");
      });
      // reject("失败");
    });
    prs.then((res) => {
      console.log(res);
    });
    //此时打印的结果为空
    
    那我们就需要一个执行器的管理数组,等状态改变的时候再去执行数组里面保存的方法

接上文说到 我们执行一个异步的时候 我们现在写的是没有打印结果的 这是因为resolve 和reject延时进入 导致此时的PromiseState的状态为Pedding 所以不会调用Promise中的resolve和reject方法,那我们就继续完善代码,当执行异步的时候应该有一个数组去管理Promise的执行队列,然后再循环执行这个数组里面的方法

  fulfilledList = []; //成功回调的管理数组
  rejectedList = []; //失败回调的管理数组
  //类中新增两个管理数组
  
  then(onFulfilled, onRejected) {
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (val) => val;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason;
          };
    // 判断状态
    if (this.PromiseState === this.FULFILLED) onFulfilled(this.PromiseResult);
    if (this.PromiseState === this.REJECTED) onRejected(this.PromiseResult);
    if (this.PromiseState == "pending") {
        //此时当PromiseState的状态为pedding时 我们根据调用.then时传去的resolve和reject去根据fulfilledList,rejectedList两个数组分别管理
      this.fulfilledList.push((value) => {
        setTimeout(() => {
          onFulfilled(value);
        }, 0);
      });
      this.rejectedList.push((reason) => {
        setTimeout(() => {
          onFulfilled(reason);
        }, 0);
      });
    }
  }
  //在resolve 和 reject再循环执行 两个数组中保存的方法
  resolve = (value) => {
    this.PromiseState = this.FULFILLED; //改变状态
    this.PromiseResult = value; //赋值
    this.fulfilledList.forEach((e) => e(value));
  };
  reject = (reason) => {
    this.PromiseState = this.REJECTED; //改变状态
    this.PromiseResult = reason; //赋值
    this.fulfilledList.forEach((e) => e(reason));
  };
  
  //此时在测试一下
  const prs = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("成功");
        // reject("失败");
      });
    });
    prs.then((res) => {
      console.log(res);
    });

正常执行了

image.png

下面开始实现Promise的链式调用

先看一下原生的Promise链式调用

new Promise((resolve, reject) => {
  resolve("成功");
})
.then(console.log(123))
.then((res) => {
  console.log(res);
});

//打印台分别打印 123 和 成功

支持链式调用 说明第一个then方法返回的一定是一个Promise,那么我们基于这个特征继续往下写

未完待续