真有你的!只需十六个步骤,就能实现一个自定义Promise!

前言

Hello~大家好! 新年伊始,先给大家拜个晚年:祝各位掘友们2024年工作顺利!身体健康!万事如意!!!

真有你的!只需十六个步骤,就能实现一个自定义Promise!

回到正题,在前端项目开发中,无论你是刚入门的小白还是“叱咤风云”多年的老鸟,一定都绕不开Promise的使用。

本篇文章对于Promise的介绍和用法就不多赘述了,我们专心来探究Promise内部实现的逻辑,阅读本篇文章的最终目的是能够实现一个自定义的Promise。

还是老样子,阅读前请确保以下几点:

  1. 熟练掌握Promise使用语法

  2. 有些同学可能不熟悉ES6中class的用法,所以本篇文章我们使用ES5中的函数和原型链来模拟ES6中的class特性

  3. 如果同学们对ES5中的原型对象和原型链知识感兴趣的话可以在评论区告诉我,我会找时间更新一篇《搞懂原型对象和原型链》

  4. 文章中涉及到this指向的知识,如果你对this指向有疑问或者不熟悉可以先看看我之前的两篇文章:

Promise 使用方法

  let p = new Promise((resolve, reject) => {
        resolve("ok");
      });

      console.log(p);

      p.then(
        (value) => {
          console.log(value);
        },
        (error) => {
          console.log(error);
        }
      );

真有你的!只需十六个步骤,就能实现一个自定义Promise!

我们从Promise最简单的一个使用方法开始,可以看到上面代码和输出结果:

  1. 通过new关键字创建了一个Promise类型的实例对象赋值为p
  2. Promise构造函数传入一个回调函数
  3. p 实例对象调用了自身的then方法

一、 搭建框架

在这一步里,我们先搭建好基础框架:

  1. 既然 p实例对象是通过new关键字创造出来的,那么Promise就是一个构造函数。

  2. Promise构造函数接收一个参数(回调函数),我们叫它执行器函数:executor

  3. p实例对象可以调用then方法,那就说明Promise的原型对象上有一个then函数,并且接收两个参数,我们命名为onResolvedonRejected


function MyPromise(executor){

};

MyPromise.prototype.then = function (onResolved,onRejected){

};

好了,第一步就大功告成了,是不是很简单?

真有你的!只需十六个步骤,就能实现一个自定义Promise!

后面我们会基于这个搭建出来的“框架”一点一点的去完善实现功能

二、同步调用执行器函数,且需要带上两个参数 reslove/reject


function MyPromise(executor){
+    // resolve 函数
+    function resolve(data){};

+    // reject 函数
+    function reject(data){}

+    //同步调用执行器函数
+    executor(resolve,reject);
}

MyPromise.prototype.then = function (onResolved,onRejected){

};

  1. Promise函数接收到了执行器以后,需要同步调用这个执行器函数executor,并且传入resolvereject参数

  2. 那么问题来了,resolvereject是什么东东呢?

  3. 回到最开始的使用案例代码里面,我们看到
    resolve("ok"); reject("faild");

  4. resolvereject其实是更改Promise状态的两个函数,两者同样都是接收一个data参数

三、 实现resolve函数和reject函数

在上一步里面我们仅仅是定义了resolve函数和reject函数,在这一步里我们来实现resolve函数和reject函数的逻辑

1. resolve函数

resolve函数的功能是什么呢?

  1. 修改状态:在用户使用resolve函数后,Promise的状态从初始化的pending状态变成fulfilled状态

  2. 设置结果值:在用户使用resolve函数后,Promise的结果值变成用户传入的数据。

function MyPromise(executor){
+    this.PromiseState = 'pending';
+    this.PromiseResult = null;
    // 保存实例对象的this值
+    const self = this;
    // resolve 函数
     function resolve(data){
+      // 1.修改对象的状态 (promiseState)
+       self.PromiseState = 'fulfilled';
         
+      // 2.设置对象结果值(promiseResult)
+       self.PromiseResult = data;
     };

    // reject 函数
    function reject(data){ }

    //同步调用执行器函数
    executor(resolve,reject);
}

  1. 需要特别注意的一点:这里的this指向要小心处理。为什么呢?我们还是回到使用案例里面看看:
 let p = new Promise((resolve, reject) => {
        resolve("ok");
      });

resolve函数是处在箭头函数里面的,这个时候调用resolve函数,this指向window
我们期望这个this是指向实例对象p身上,所以需要提前保存this指向。

2. reject函数

reject函数和resolve函数是同样的逻辑,不同的是状态更改为rejected

function MyPromise(executor){
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // 保存实例对象的this值
    const self = this;
    // resolve 函数
    function resolve(data){
      // 1.修改对象的状态 (promiseState)
       self.PromiseState = 'fulfilled';
         
     // 2.设置对象结果值(promiseResult)
       self.PromiseResult = data;
    };

+    // reject 函数
+    function reject(data){
+        // 1.修改对象的状态 (promiseState)
+        self.PromiseState = 'rejected';
         
+        // 2.设置对象结果值(promiseResult)
+        self.PromiseResult = data;
+    }

    //同步调用执行器函数
    executor(resolve,reject);
}

四、 处理抛出异常情况

  1. 在执行器函数里面,除了通过resolvereject函数可以改变Promise的状态。使用throw关键字抛出异常也可以改变状态。我们先看下面的原生Promise例子效果:
 let p = new Promise((resolve, reject) => {
        throw "error msg";
      });

      console.log(p);

      p.then(
        (value) => {
          console.log("res:", value);
        },
        (error) => {
          console.log("error:", error);
        }
      );

真有你的!只需十六个步骤,就能实现一个自定义Promise!

  1. 可以看到,如果在执行器函数内部中抛出了错误,那么Promise的状态就会变成rejected,且then函数中就会执行失败的回调函数。问题来了,如何实现这一功能呢?

  2. 其实非常简单,我们使用try catch语句包裹执行器函数executor,当catch到错误的时候,执行reject函数,将Promise的状态改为失败状态,将error值当成Promise的结果值。



function MyPromise(executor) {
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // 保存实例对象的this值
    const self = this;
    // resolve 函数
    function resolve(data) {
        // 1.修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';

        // 2.设置对象结果值(promiseResult)
        self.PromiseResult = data;
    };

    // reject 函数
    function reject(data) {
        // 1.修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';

        // 2.设置对象结果值(promiseResult)
        self.PromiseResult = data;
    }

    // 处理异常
+    try {
+        //同步调用执行器函数
+        executor(resolve, reject);
+    } catch (error) {
+        // 修改promise状态为失败
+        reject(e)
+    }

}

五、 promise状态只能改变一次

目前我们自定义的Promise有一个非常大的问题,就是Promise的状态可以多次改变。

 let p = new MyPromise((resolve, reject) => {
       reject('error');
       resolve('ok');
      });

      console.log(p);

真有你的!只需十六个步骤,就能实现一个自定义Promise!

  • 可以看到,Promise的状态从失败状态又变成了成功状态,这明显是不符合Promise的特点的。

  • 所以我们在resolve函数和reject函数中要加入一个判断限制:

    • Promise的状态只能从 pending 变成 fulfilled 状态

    • Promise的状态只能从 pending 变成 rejected 状态

function MyPromise(executor) {
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // 保存实例对象的this值
    const self = this;
    // resolve 函数
    function resolve(data) {
+       if(self.PromiseState !== 'pending') return;
        // 1.修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';

        // 2.设置对象结果值(promiseResult)
        self.PromiseResult = data;
    };

    // reject 函数
    function reject(data) {
+       if(self.PromiseState !== 'pending') return;
        // 1.修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';

        // 2.设置对象结果值(promiseResult)
        self.PromiseResult = data;
    }

    // 处理异常
    try {
        //同步调用执行器函数
        executor(resolve, reject);
    } catch (error) {
        // 修改promise状态为失败
        reject(e)
    }

}
  

六、 实现then方法功能

1.执行回调函数

then方法的第一个功能就是根据Promise状态调用不同的回调函数, 且在onResolved方法和onRejected方法中加入参数(PromiseResult)

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

+    if(this.PromiseState === 'rejected'){
+            onRejected(this.PromiseResult);
+    }
+ }

2. 异步任务回调的处理

我们来看一个例子:

 let p = new MyPromise((resolve, reject) => {
        setTimeout(function () {
          resolve("ok");
        }, 1000);
      });

      console.log(p);

      p.then(
        (value) => {
          console.log("res:", value);
        },
        (error) => {
          console.log("error:", error);
        }
      );

真有你的!只需十六个步骤,就能实现一个自定义Promise!

  1. 可以看到,Promise还是pending状态,并且没有执行任何一个回调函数。这是为什么呢?

  2. 如果是在正常同步代码逻辑执行时, resolve或者reject函数后,状态会改变。接着执行then方法,then方法会根据状态,调用不同的回调函数。

  3. 但是在异步的时候,计时器1秒钟后才会改变状态,在这1秒钟之前,如果执行了then函数,此时状态是pending,then方法不知道调用哪个回调函数。

  4. 所以我们在then方法里面,加入一个pending状态的判断,如果执行then函数时,Promise状态还没改变,就先把这两个回调函数保存起来。

  5. 等到调用resolve函数或者reject函数时,再取出来根据状态执行不同的回调函数

function MyPromise(executor) {
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // 保存回调函数
+   this.callback = {};
    // 保存实例对象的this值
    const self = this;
    // resolve 函数
    function resolve(data) {
        if (self.PromiseState !== 'pending') return;
        // 1.修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';

        // 2.设置对象结果值(promiseResult)
        self.PromiseResult = data;
        // 执行成功的回调函数
+       if (self.callback.onResolved) {
+            self.callback.onResolved(data);
+        }
    };

    // reject 函数
    function reject(data) {
        if (self.PromiseState !== 'pending') return;
        // 1.修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';

        // 2.设置对象结果值(promiseResult)
        self.PromiseResult = data;
        // 执行失败的回调函数
+        if (self.callback.onRejected) {
+            self.callback.onRejected(data);
+       }
    }

    // 处理异常
    try {
        //同步调用执行器函数
        executor(resolve, reject);
    } catch (error) {
        // 修改promise状态为失败
        reject(e)
    }

}


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
+        }
+    }

}

七、指定多个回调函数的实现

我们继续看下一个例子,给MyPromise指定多个then函数:

  let p = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          resolve("ok");
        }, 1000);
      });

      console.log(p);

      p.then((value) => {
        console.log("res1:", value);
      });

      p.then((value) => {
        console.log("res2:", value);
      });

      p.then((value) => {
        console.log("res3:", value);
      });

      p.then((value) => {
        console.log("res4:", value);
      });


真有你的!只需十六个步骤,就能实现一个自定义Promise!

  1. executor中如果是异步操作,这时候指定了多个then函数,那么在第二个then函数执行的时候,第二个 then里面的成功回调函数,会覆盖之前的回调函数。

  2. 同理,第三个then里面的成功回调函数会覆盖前面第二个then指定的回调函数。以此类推。

  3. 如何解决这个问题呢?非常简单。只需要将原来callback对象改为数组即可


function MyPromise(executor) {
    this.PromiseState = 'pending';
    this.PromiseResult = null;
+   this.callbacks = [];
    // 保存实例对象的this值
    const self = this;
    // resolve 函数
    function resolve(data) {
        if (self.PromiseState !== 'pending') return;
        // 1.修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';

        // 2.设置对象结果值(promiseResult)
        self.PromiseResult = data;

        // 调用成功的回调函数
+        self.callbacks.forEach(item=>{
+            if (item.onResolved) {
+                item.onResolved(data);
+            }
+        })
    };

    // reject 函数
    function reject(data) {
        if (self.PromiseState !== 'pending') return;
        // 1.修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';

        // 2.设置对象结果值(promiseResult)
        self.PromiseResult = data;

        // 调用成功的失败函数
+        self.callbacks.forEach(item=>{
+            if (item.onRejected) {
+                item.onRejected(data);
+            }
+        })


    }

    // 处理异常
    try {
        //同步调用执行器函数
        executor(resolve, reject);
    } catch (error) {
        // 修改promise状态为失败
        reject(e)
    }

}


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.callbacks.push({
+            onResolved,
+            onRejected
+        })
+    }

}

八、对then方法的返回结果进行处理

  1. then方法是可以有返回值的,且返回值是一个新的Promise类型的实例对象。

  2. 那么问题来了,这个新的Promise类型的实例对象的结果值是什么呢?

  3. 答案:结果是由then函数选择执行哪一个回调函数的结果来决定的。

  4. 下面我们分为两种情况进行分析:

1. 同步情况

先看下面的例子:

then函数返回值不为Promise类型

 let p = new Promise((resolve, reject) => {
           resolve("ok");
       });

      const result =  p.then((value) => {
         console.log("res:", value);
         // 相当于 return undefined
       });

       console.log(result)

真有你的!只需十六个步骤,就能实现一个自定义Promise!

  • 可以看到在执行成功的回调函数以后,return了一个undefined,此时result是一个新的Promise实例对象,状态为成功且值为undefined

then函数返回值为Promise类型


let p = new Promise((resolve, reject) => {
           resolve("ok");
       });

const result =  p.then((value) => {
         console.log("res:", value);
         return new Promise((resolve)=>{
          resolve("okok")
      })
});

真有你的!只需十六个步骤,就能实现一个自定义Promise!

  • 可以看到在执行成功的回调函数以后,return了一个新的Promise对象,此时result是一个新的Promise实例对象,状态为成功值且为 ‘okok’

代码功能完善:

  • 首先要修改then方法的返回值: 返回值应该是一个新的Promise实例对象: (return new Promise())
  • 获取回调函数的执行结果: let result = onResolved(this.PromiseResult);
  • 判断结果类型:
    • 如果是Promise类型。then方法里面可能有抛出异常,还需要加入try..catch对异常进行处理
    • 如果不是Promise类型,直接将状态变为成功的状态。

function MyPromise(executor) {
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    this.callbacks = [];
    // 保存实例对象的this值
    const self = this;
    // resolve 函数
    function resolve(data) {
        if (self.PromiseState !== 'pending') return;
        // 1.修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';

        // 2.设置对象结果值(promiseResult)
        self.PromiseResult = data;

        // 调用成功的回调函数
        self.callbacks.forEach(item => {
            if (item.onResolved) {
                item.onResolved(data);
            }
        })
    };

    // reject 函数
    function reject(data) {
        if (self.PromiseState !== 'pending') return;
        // 1.修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';

        // 2.设置对象结果值(promiseResult)
        self.PromiseResult = data;

        // 调用成功的失败函数
        self.callbacks.forEach(item => {
            if (item.onRejected) {
                item.onRejected(data);
            }
        })


    }

    // 处理异常
    try {
        //同步调用执行器函数
        executor(resolve, reject);
    } catch (error) {
        // 修改promise状态为失败
        reject(e)
    }

}


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(res => {
                        resolve(res);
                    }, err => {
                        reject(err)
                    })
                } else {
                    // 结果的对象状态为成功
                    resolve(result);
                }
            } catch (error) {
                reject(error)
            }

        }

        if (this.PromiseState === 'rejected') {
            let result = onRejected(this.PromiseResult);
        }

        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved,
                onRejected
            })
        }

    })
}

异步情况

异步情况是指在执行器函数中出现了异步代码,请看下面原生Promise代码:

let p = new Promise((resolve, reject) => {
           setTimeout(()=>{
            resolve("ok");
           }, 1000)
       });

      const result =  p.then((value) => {
         console.log("res:", value);
       });

       console.log(result)

真有你的!只需十六个步骤,就能实现一个自定义Promise!

我们替换成自定义的MyPromise你会发现并没有满足我们的期望,此时的状态还是pending

 let p = new MyPromise((resolve, reject) => {
           setTimeout(()=>{
            resolve("ok");
           }, 1000)
       });

      const result =  p.then((value) => {
         console.log("res:", value);
       });

       console.log(result)

真有你的!只需十六个步骤,就能实现一个自定义Promise!

为了更好的说明原因, 请看下面的图:

真有你的!只需十六个步骤,就能实现一个自定义Promise!

  1. 当计时器执行的时候,此时还没到1秒,Promise的状态还是pending
  2. 那么在执行then函数的时候,是不是就进入pending状态的判断逻辑里面。在这段逻辑里面,代码知识往callbacks数组里面添加了回调函数,并没有改变状态。
  3. 此时then函数return了一个新的Promise对象给result,那这个result不就是pending状态了吗?

完善代码

知道了原因以后,我们继续对代码进行改造:

  1. then函数中,如果是pending状态,在pushcallbacks数组时,同时执行这个回调函数

  2. 获取到这个回调函数的结果

  3. 对这个结果进行判断是否是Promise对象类型

  4. 同样的,我们需要注意this指向的问题

        function MyPromise(executor) {
         this.PromiseState = 'pending';
         this.PromiseResult = null;
         this.callbacks = [];
         // 保存实例对象的this值
         const self = this;
         // resolve 函数
         function resolve(data) {
             if (self.PromiseState !== 'pending') return;
             // 1.修改对象的状态 (promiseState)
             self.PromiseState = 'fulfilled';

             // 2.设置对象结果值(promiseResult)
             self.PromiseResult = data;


             // 调用成功的回调函数
             self.callbacks.forEach(item => {
                 if (item.onResolved) {
                     item.onResolved(data);
                 }
             })
         };

         // reject 函数
         function reject(data) {
             if (self.PromiseState !== 'pending') return;
             // 1.修改对象的状态 (promiseState)
             self.PromiseState = 'rejected';

             // 2.设置对象结果值(promiseResult)
             self.PromiseResult = data;

             // 调用成功的失败函数
             self.callbacks.forEach(item => {
                 if (item.onRejected) {
                     item.onRejected(data);
                 }
             })


         }

         // 处理异常
         try {
             //同步调用执行器函数
             executor(resolve, reject);
         } catch (error) {
             // 修改promise状态为失败
             reject(e)
         }

     }


     MyPromise.prototype.then = function (onResolved, onRejected) {
+          const self = this;
         return new MyPromise((resolve, reject) => {
             if (this.PromiseState === 'fulfilled') {
                 try {
                     let result = onResolved(this.PromiseResult);
                     if (result instanceof MyPromise) {
                         result.then(res => {
                             resolve(res);
                         }, err => {
                             reject(err)
                         })
                     } else {
                         // 结果的对象状态为成功
                         resolve(result);
                     }
                 } catch (error) {
                     reject(error)
                 }

             }

             if (this.PromiseState === 'rejected') {
                 let result = onRejected(this.PromiseResult);
             }

             if (this.PromiseState === 'pending') {
+                  this.callbacks.push({
+                      onResolved: function () {
+                          try {
+                              let result = onResolved(self.PromiseResult);
+                              if (result instanceof MyPromise) {
+                                  result.then(res => {
+                                      resolve(res);
+                                  }, err => {
+                                      reject(err)
+                                  })
+                              } else {
+                                  // 结果的对象状态为成功
+                                  resolve(result);
+                              }
+                          } catch (error) {
+                              reject(error)
+                          }
+
+                      },
+                      onRejected: function () {
+                          try {
+                              let result = onRejected(self.PromiseResult);
+                              if (result instanceof MyPromise) {
+                                  result.then(res => {
+                                      resolve(res);
+                                  }, err => {
+                                      reject(err)
+                                  })
+                              } else {
+                                  // 结果的对象状态为成功
+                                  resolve(result);
+                              }
+                          } catch (error) {
+                              reject(error)
+                          }
+                      }
+                  })
+              }
+          })
+      }

九、完善then函数rejected状态的处理

  • 细心的同学会发现,在then函数里面,我们之前只对fulfilled状态进行了处理,对rejected状态还没完善逻辑。

  • 其实rejected状态中的逻辑跟fulfilled状态的处理逻辑是相似的,并且在pending状态中,充满了各种冗余相似的代码,如下图所示。所以这一节我们将代码进行一个封装和完善。

真有你的!只需十六个步骤,就能实现一个自定义Promise!

   function MyPromise(executor) {
          this.PromiseState = 'pending';
          this.PromiseResult = null;
          this.callbacks = [];
          // 保存实例对象的this值
          const self = this;
          // resolve 函数
          function resolve(data) {
              if (self.PromiseState !== 'pending') return;
              // 1.修改对象的状态 (promiseState)
              self.PromiseState = 'fulfilled';

              // 2.设置对象结果值(promiseResult)
              self.PromiseResult = data;

              // 调用成功的回调函数
              self.callbacks.forEach(item => {
                  if (item.onResolved) {
                      item.onResolved(data);
                  }
              })
          };

          // reject 函数
          function reject(data) {
              if (self.PromiseState !== 'pending') return;
              // 1.修改对象的状态 (promiseState)
              self.PromiseState = 'rejected';

              // 2.设置对象结果值(promiseResult)
              self.PromiseResult = data;

              // 调用成功的失败函数
              self.callbacks.forEach(item => {
                  if (item.onRejected) {
                      item.onRejected(data);
                  }
              })


          }

          // 处理异常
          try {
              //同步调用执行器函数
              executor(resolve, reject);
          } catch (error) {
              // 修改promise状态为失败
              reject(e)
          }

      }


      MyPromise.prototype.then = function (onResolved, onRejected) {
          const self = this;
          return new MyPromise((resolve, reject) => {
            // 封装工具函数
 +           function callbackUtil(type){
 +                   try {
 +                     let result = type(self.PromiseResult);
 +                     if (result instanceof MyPromise) {
 +                         result.then(res => {
 +                             resolve(res);
 +                         }, err => {
 +                             reject(err)
 +                         })
 +                     } else {
 +                         // 结果的对象状态为成功
 +                         resolve(result);
 +                     }
 +                 } catch (error) {
 +                     reject(error)
 +                 }
 +               }

              if (this.PromiseState === 'fulfilled') {
 +               callbackUtil(onResolved)
              }

              if (this.PromiseState === 'rejected') {
  +              callbackUtil(onRejected)
              }

              if (this.PromiseState === 'pending') {
                  this.callbacks.push({
 +                     onResolved: function () {
 +                       callbackUtil(onResolved)
                      },
 +                    onRejected: function () {
 +                      callbackUtil(onRejected)
                      }
                  })
              }
          })
      }


十、完善catch方法与解决异常穿透问题

catch方法

  • catch方法的功能:Promise 对象的 catch() 方法用于注册一个在 promise 被拒绝时调用的函数。
  • 官方的定义稍微有点拗口,简单来说就是当Promise状态称为rejected时,这个catch方法就会执行

继续看一个原生Promise的效果:

let p = new Promise((resolve, reject) => {
                       setTimeout(()=>{
                        reject("error");
                       }, 1000)
                   });

      p.catch(err => {
        console.log(err) // error
      })

我们继续完善catch方法:

+  MyPromise.prototype.catch = function (onRejected) {
+             return this.then(undefined, onRejected);
+   };

异常穿透

先看原生Promise的效果,在then方法中指定回调函数时,是可以允许用户不传入成功或者失败的回调函数。

 let p = new Promise((resolve, reject) => {
                             setTimeout(()=>{
                               resolve("ok");
                             }, 1000)
                         });

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

完善代码

   MyPromise.prototype.then = function (onResolved, onRejected) {
                    const self = this;
                     //判断回调函数参数
+                    if (typeof onRejected !== "function") {
+                      onRejected = (reason) => {
+                        throw reason;
+                      };
+                    }

+                    if (typeof onResolved !== "function") {
+                      onResolved = (value) => value;
+                    }
                    return new MyPromise((resolve, reject) => {
                          // 封装函数
                      function callbackUtil(type){
                              try {
                                let result = type(self.PromiseResult);
                                if (result instanceof MyPromise) {
                                    result.then(res => {
                                        resolve(res);
                                    }, err => {
                                        reject(err)
                                    })
                                } else {
                                    // 结果的对象状态为成功
                                    resolve(result);
                                }
                            } catch (error) {
                                reject(error)
                            }
                          }

                        if (this.PromiseState === 'fulfilled') {
                          callbackUtil(onResolved)
                        }

                        if (this.PromiseState === 'rejected') {
                          callbackUtil(onRejected)
                        }

                        if (this.PromiseState === 'pending') {
                            this.callbacks.push({
                                onResolved: function () {
                                  callbackUtil(onResolved)
                                },
                                onRejected: function () {
                                  callbackUtil(onRejected)
                                }
                            })
                        }
                    })
                }



十一、实现 Promise.resolve方法

  1. resolve函数返回的是一个新的Promise类型的实例对象
  2. resolve函数如果接收的参数是普通数据类型,那么返回的实例对象状态为成功fulfilled状态
  3. resolve函数如果接收的参数是Promise类型的实例对象,需要判断是什么状态。
  4. 如果是成功状态,那么resolve函数返回的实例对象状态为成功fulfilled状态
  5. 如果是失败状态,那么resolve函数返回的实例对象状态为成功rejected状态
MyPromise.resolve = function (value) {
  return new MyPromise((resolve, reject) => {
    if (value instanceof MyPromise) {
      value.then(
        (v) => {
          resolve(v);
        },
        (e) => {
          reject(e);
        }
      );
    } else {
      resolve(value);
    }
  });
};

十二、实现Promise.reject方法

  1. reject方法永远都是返回失败的Promise实例对象,状态为rejected
MyPromise.reject = function (value) {
 return new MyPromise((resolve, reject) => {
   reject(value);
 });
};

十三、实现Promise.all方法

  • 当传入一个 Promise 对象的数组时,Promise.all 方法会返回一个新的 Promise。
  • 这个新的 Promise 在所有输入的 Promise 都成功(resolve)后才会 resolve,并且其结果是一个包含每个输入 Promise 结果值的数组,顺序与输入数组相同。
  • 如果任何一个输入的 Promise 被拒绝(reject),那么 Promise.all 返回的 Promise 会立即 reject,并返回第一个被拒绝的 Promise 的拒绝原因。
MyPromise.all = function (promises) {

  return new MyPromise((resolve, reject) => {
    // 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise
    if (promises.length === 0) return resolve(promises);
    let count = 0;
    let result = [];
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(
        (v) => {
          //只要执行了当前函数,就代表当前promise对象成功了
          //条件:必须每个promise对象都成功才能执行resolve
          count++;
          //将当前promise对象结果存入数组,直接使用push()方法会有小瑕疵,无法固定顺序
          result[i] = v;
          if (count === promises.length) {
            resolve(result);
          }
        },
        (e) => {
          reject(e);
        }
      );
    }
  });
};

十四、实现 Promise.allsettled 方法

  • Promise.allSettled 同样接受一个可迭代对象,并返回一个新的 Promise 对象。
  • 返回的 Promise 对象在所有传入的 Promise 都已经解决(无论成功还是失败)后才会被解决。该 Promise 的解决值是一个数组,包含所有传入的 Promise 的结果,每个结果都是一个对象,包含 status 字段表示状态(”fulfilled” 表示成功,”rejected” 表示失败),以及相应的 valuereason 字段表示值或拒绝原因。

MyPromise.allSettled = function (promises) {
  return new MyPromise((resolve, reject) => {
    if (promises.length === 0) return resolve(promises);
    let count = 0;
    let result = [];
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(
        (v) => {
          //只要执行了当前函数,就代表当前promise对象成功了
          count++;
          //将当前promise对象结果存入数组,直接使用push()方法会有小瑕疵,无法固定顺序
          result[i] = v;
          if (count === promises.length) {
            resolve(result);
          }
        },
        (e) => {
          count++;
          result[i] = e;
          if (count === promises.length) {
            resolve(result);
          }
        }
      );
    }
  });
};
  


十五、实现 Promise.race方法

  1. 注意: promise数组中如果有某个promise对象是异步的,需要判断哪个promise对象最先执行
MyPromise.race = function (promises) {
 return new MyPromise((resolve, reject) => {
   for (let i = 0; i < promises.length; i++) {
     promises[i].then(
       (v) => {
         resolve(v);
       },
       (e) => {
         reject(e);
       }
     );
   }
 });
};

十六、完善then方法中的回调函数异步执行情况

什么意思呢?我们用原生的Promise看看效果就清楚了:

                let p = new Promise((resolve, reject) => {
                              resolve('ok');
                              console.log(111)
                });
                
                p.then(res => {
                    console.log(222);
                })

                console.log(333)
             // 执行顺序  111->333->222

所以我们在执行回调函数时,也需要进行异步处理,很简单,加上setTimeout包裹一下就能实现

结语

  • 好了,没骗你们吧!!!

  • 十六个步骤以后,我们就已经实现了基础版 的自定义Promise了,为什么说是基础版呢?因为在许多方法实现上我们没有做一些参数数据的校验,在某些判断语句上也有处理比较粗糙的情况出现。

  • 用当前这个代码去跑 Promises/A+ 规范的测试用例当然是无法全部通过的。

  • 当然这也无伤大雅,最重要的是能够打消同学们对Promise的恐惧 并且能快速搞懂Promise的核心实现原理

  • 如果有追求完美的同学可以继续照着 Promises/A+ 规范和使用ES6中的class 来完善我们的自定义Promise

  • 最后谢谢大家的观看,如果有帮助到你~希望能得到你的点赞收藏评论!

  • 大家也可以移步我的主页,有其他文章供大家阅读:秋天的一阵风

真有你的!只需十六个步骤,就能实现一个自定义Promise!

原文链接:https://juejin.cn/post/7336871908085989427 作者:秋天的一阵风

(0)
上一篇 2024年2月20日 下午4:06
下一篇 2024年2月20日 下午4:17

相关推荐

发表回复

登录后才能评论