从零手写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>

  • 能够看的到执行构造是异步的,第一步胜利了。

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

  • 1、首先是通过es5写的,所以须要导出咱们应用了 闭包

  • 2、 创立Promise构造函数 并导出

  • 3、咱们应用promise时都晓得往里面传入一个函数,这个函数带有resolve,reject并且在函数外面执行须要执行的操作,

所以这个承受的参数叫做执行器(executor)

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

  • 5、咱们都晓得promise有三个状态其中初始化时pendding

  • 6、依据resolve,reject扭转状态,扭转值

  • 7、咱们都晓得promise构造函数还有then办法和catch办法

  • 8、别忘了promise也能够throw Error,所以执行器(executor)须要批改一下

  • 9、最初调用并执行看看后果
<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的后果决定        // 1、抛出异样MyPromise后果为失败      reason为后果        // 2、返回的是MyPromise               MyPromise为以后的后果        // 3、返回的不是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的实现,略微比下面难一些。
  • 好了接下来我解说一下
  • 1、首先是在原来的根底下来定义了一些常量方便使用

  • 2、要晓得Promise.then返回的是什么

  • 3、接着判断一下状态

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

  • 5、能够看到pendding状态是没有增加定时器的,为什么不是异步呢,因为是往callbacks存储,而后下面执行到对应地位会进行循环判断,且在循环操作时是异步的,,欠缺pendding

  • 6、后面的代码能够看得出有四个地位代码类似度很高,所以定义一个函数,进行封装(留神要在new Promise 里)

  • 7、再设置一下默认值

  • 8、再来欠缺一下catch

接下来咱们进行测试

  • 看来没为什么问题
  • 再来试试catch


  • 老手第一次写的还是不怎么样,然而每一步都是通过本人是如何学,实实在在的敲进去的,心愿可能帮忙到和我一样想学好promise的敌人。从开始的很难到前面能间接写进去,其实破费的工夫还是不少的,然而收货颇丰啊。