特皮技术团队:一年前端手写Promise

从零手写Promise

  • 在这个前端快速发展的时代,今天不努力,明天变垃圾,虽然这是一个表情包上看到的,相信你们必定都看过,说实在的,这话是粗,可是事实就是如此。
  • 言归正传咱们看看今天的内容把。
  • 上次咱们看了js的重点也是难点 “异步”,趁热打铁,今天看看从零手写Promise

咱们先看看代码
首先咱们先来回顾一下Promise的用法前端

new Promise((resolve,reject)=>{
    resolve('正确的')
}).then(value =>{
    console.log(value);
})
  • 能够看的到在浏览器打印出了 "正确的"Promise的用法并不难,咱们只要知道 new Promise Promise传入function(resolve,reject),接着在函数内作须要作的事情便可,等到须要的时候,对这个构造函数.then(res=>{})便可获取对应的值

下面咱们就来分析一下Promise应该如何实现

  • 首先咱们应该定义一个Promise的结构
  • 本文觉得es5 的方式
(function (window) {
    function MyPromise(executor) {                                          // 1  定义MyPromise 构造函数
            
        function resolve(value) {                                           // 定义resolve                               
        }
        
        function reject(reason) {                                           // 定义reject  
        }

        MyPromise.prototype.then = function (onResolved,onRejected) {       // 定义then
        }

        MyPromise.prototype.catch = function (error) {                      // 定义catch
        }

        // 定义执行器的执行
        executor(resolve,reject);

    }
    
    window.MyPromise = MyPromise;                  // 2导出
    
})(window)
  • 接着能够看看下面的执行
(function (window) {
    // Promise  
    function Mypromise(executor) {
        const self = this;                                  // 须要定义一下this指向
        self.status = 'pennding';                           // 初始化状态
        self.data = undefined;                              // promise对象指定一个用于存储结果数据
        self.callbacks = [];                                // 每一个元素的结构  { onResolved(){}, onRejected(){}}

        function resolve(value) {                           // resolve
            if (self.status !== 'pennding') {               // 由于promise 状态只能修改一次
                return;
            }
            self.status = 'resolve';                        // 改变为 resolve                  
            self.data = value;                              // 保存value的值                        
            if (self.callbacks.length > 0) {                // 若是有待执行的callback函数,当即执行回调函数onResolved
                setTimeout(() => {                          // 当前方案是为了将任务挂在队列中,制造异步
                    self.callbacks.forEach(callbacksObj => {
                        callbacksObj.onResolved(value)
                    })
                },0)
            }
        }

        function reject(reason) {                            //reject
            if (self.status !== 'pennding') {                // 由于promise 状态只能修改一次
                return;
            }
            self.status = 'reject';                          // 改变为 reject
            self.data = reason;                              // 保存reason的值                        
            if (self.callbacks.length > 0) {                 // 若是有待执行的callback函数,当即执行回调函数onRejected
                setTimeout(() => {
                    self.callbacks.forEach(callbacksObj => {
                        callbacksObj.onRejected(reason)
                    })
                },0)
            }
        }

        try {                                                 // 若是执行器抛出异常
            executor(resolve, reject);
        } catch (error) {
            reject(error)
        }
    }

    // Promise.then()
    Mypromise.prototype.then = function (onResolved, onRejected) {
        // 假设当前状态仍是pennding 
        const self = this;
        self.callbacks.push({
            onResolved,
            onRejected
        })

    }

    // Promise.catch()
    Mypromise.prototype.carch = function (error) {

    }

    window.Mypromise = Mypromise;
})(window);
<body>
    <script src="./promise.js"></script>
    <script>

        const p = new Mypromise((resolve, reject) => {
            setTimeout(() => {           // 由于拆分 then还未处理,须要p.then 先执行
                resolve(1)
                console.log("我先")
            }, 100)
        })

        p.then(value => {
            console.log("onResolve()1", value)
        }, reason => {
            console.l("onReject()1", reason)
        })

        p.then(value => {
            console.log("onResolve()2", value)
        }, reason => {
            console.l("onReject()2", reason)
        })
    </script>
</body>

  • 能够看的到执行结构是异步的,第一步成功了。

上面的代码咱们来说解一下,

  • 一、首先是经过es5写的,因此须要导出咱们使用了 闭包

  • 二、 建立Promise构造函数 并导出

  • 三、咱们使用promise时都知道往里面传入一个函数,这个函数带有resolve,reject而且在函数里面执行须要执行的操做,

因此这个接受的参数叫作执行器(executor)promise

  • 四、执行器里执行的是两个函数

  • 五、咱们都知道promise有三个状态其中初始化时pendding

  • 六、根据resolve,reject改变状态,改变值

  • 七、咱们都知道promise构造函数还有then方法和catch方法

  • 八、别忘了promise也能够throw Error,因此执行器(executor)须要修改一下

  • 九、最后调用并执行看看结果
<body>
    <script src="./promise1.js"></script>
    <script>

        const p = new MyPromise((resolve, reject) => {
            setTimeout(() => {            // 由于拆分缘由then 还未作对应处理,此时不能改变状态
                resolve(1)
                console.log("我先")
            }, 100)
        })

        p.then(value => {
            console.log("onResolve()1", value)
        }, reason => {
            console.l("onReject()1", reason)
        })

        p.then(value => {
            console.log("onResolve()2", value)
        }, reason => {
            console.l("onReject()2", reason)
        })
    </script>
</body>

  • 怎么结果不对呢
  • 再来看看 由于直接执行的回调是同步执行的,须要将任务放入队列中

  • 此时结果正确了。
  • 最后别忘了promise状态只容许修改一次

  • 好的上面是简易版的Promise并不包括then的实现。

接下来咱们在上面的基础对then以及总体进行了一个升级

  • 代码以下
(function (window) {
  const PENDDING = 'pendding';
  const FULFILLED = 'fulfilled';
  const REJECTED = 'rejected';

  function MyPromise(executor) {                                          // 定义MyPromises构造函数
    const self = this;
    self.status = PENDDING;
    self.data = undefined;
    self.callbacks = [];

    function resolve(value) {
      if (self.status === PENDDING) {
        self.status = FULFILLED;                                           // 改变MyPromise状态
        self.data = value;                                                 // MyPromise 的值对应变化            
        setTimeout(() => {                                                 // 异步执行 
          self.callbacks.forEach(callbacksObj => {                         // 若是有待执行的callback函数,当即执行回调
            callbacksObj.onResolved(value)                              
          })
        })
      }
    }

    function reject(reason) {
      if (self.status === PENDDING) {
        self.status = REJECTED;
        self.data = reason;
        setTimeout(() => {
          self.callbacks.forEach(callbacksObj => {
            callbacksObj.onRejected(reason);
          });
        })
      }
    }

    try {                                                                   // MyPromise能够抛出异常
      executor(resolve, reject);
    } catch (error) {
      reject(error)
    }
  }
  /* 
  MyPromise原型对象的then()
  指定成功和失败回调
  返回一个新的回调函数
  // 返回的MyPromise结果由onResolved/onRejected的结果决定
  */
  MyPromise.prototype.then = function (onResolved, onRejected) { // 定义then      
    const self = this;
    // 指定回调函数的默认值(必须是函数)
    onResolved = typeof onResolved==='function' ?  onResolved : value => value;
    onRejected = typeof onRejected==='function' ?  onRejected : reason => {throw reason};

    return new MyPromise((resolve,reject)=>{                    // 返回一个新的MyPromise对象
      function handle(callback) {
          // 返回的MyPromise结果由onResolved/onRejected的结果决定
        // 一、抛出异常MyPromise结果为失败      reason为结果
        // 二、返回的是MyPromise               MyPromise为当前的结果
        // 三、返回的不是MyPromise             value为结果

        // 须要经过捕获获取才能知道有没有异常
        try{
          const result = callback(self.data)
          // 判断是否是MyPromise
          if ( result instanceof MyPromise){
            // 只有then才知道结果
            result.then(value=>resolve(value),reason=>reject(reason))
          }else{
            resolve(result)
          }
        }catch(error){
          reject(error)                                          // 返回的结果为reject(error)  上面第一点
        }
      }
    
      // 判断当前的status
      if (self.status === FULFILLED){                           // 状态为 fulfilled
        setTimeout(()=>{                                        // 当即执行异步回调
          handle(onResolved);
        })                                                        
      } else if (self.status === REJECTED){                     // 状态为 rejected
        setTimeout(()=>{                                        // 当即执行异步回调
          handle(onRejected);
        })
      }else{                                                    // pendding将成功和失败保存到callbacks里缓存起来
        self.callbacks.push({
          onResolved(value){                                    //函数里面调用回调函数 而且根据回调函数的结果改变MyPromise的结果
            handle(onResolved)                                  //为何这里没有setTimeout,由于上面已经写到过改变状态后回去callbacks里循环待执行的回调函数
          },
          onRejected(reason){
            handle(onRejected)
          }
        })
      }
    })
  }

  MyPromise.prototype.catch = function (onRejected) {               //定义then
    return this.then(undefined,onRejected)
  }
  window.MyPromise = MyPromise; // 导出MyPromise
})(window)
  • 相信你们看到这个代码跟我是同样的反应,我去,怎么跟以前不同了
  • 实际上当我动手去实现了手写Promise之后我以为真的并不难,若是要说难这个then的实现,稍微比上面难一些。
  • 好了接下来我讲解一下
  • 一、首先是在原来的基础上去定义了一些常量方便使用

  • 二、要知道Promise.then返回的是什么

  • 三、接着判断一下状态

  • 四、好的接下来咱们对相应的状态进行操做

  • 五、能够看到pendding状态是没有添加定时器的,为何不是异步呢,由于是往callbacks存储,而后上面执行到对应位置会进行循环判断,且在循环操做时是异步的,,完善pendding

  • 六、前面的代码能够看得出有四个位置代码类似度很高,因此定义一个函数,进行封装(注意要在new Promise 里)

  • 七、再设置一下默认值

  • 八、再来完善一下catch

接下来咱们进行测试

  • 看来没为何问题
  • 再来试试catch


  • 新手第一次写的仍是不怎么样,可是每一步都是经过本身是如何学,实实在在的敲出来的,但愿可以帮助到和我同样想学好promise的朋友。从开始的很难到后面能直接写出来,其实花费的时间仍是很多的,可是收货颇丰啊。
相关文章
相关标签/搜索