美文网首页前端修仙之路
ES6-promise学习及手写promise

ES6-promise学习及手写promise

作者: 月上秦少 | 来源:发表于2019-10-09 22:33 被阅读0次
    image

    1. promise要解决的问题:

    脑筋急转弯:把牛关进冰箱里,要分几步?

    // 第一步,打开冰箱
    function open(){
        setTimeout(()=>{
            console.log('打开冰箱');
            return 'success';
        }, 1000)
    }
    
    // 第二步,放牛进去
    function settle(){
          setTimeout(()=>{
           console.log('放牛进去');
           return 'success';
        }, 3000)
    }
    
    // 第三步,关上冰箱
    function close(){
          setTimeout(()=>{
           console.log('关上冰箱');
           return 'success';
        }, 1000)
    }
    
    function closeCow(){
        open();
        settle();
        close()
    }
    
    closeCow();
    
    //"打开冰箱"
    //"关上冰箱"?
    //"放牛进去"?
    

    很显然,这三个操作不能颠倒顺序,否则任务就会失败。但是上述逻辑并不能保证最终是按照我们想要的顺序进行,显然,难点在于第二步,花费的时间更长。为了保证第二步在第一步执行成功之后再执行,第三步在第二步执行成功之后在执行,改进:

    function closeCow() {
        setTimeout(() => {
            console.log("打开冰箱");
            setTimeout(() => {
                console.log("放牛进去");
                setTimeout(() => {
                    console.log("关闭冰箱");
                }, 1000);
            }, 3000);
        }, 1000);
    }
    

    这样的确解决了问题,但是看起来很别扭,逻辑不清晰,这就是经典的“回调地狱”。在过去,要想做多重的异步操作,会导致经典的回调地狱,promise的出现,就是为了解决这个问题的。

    Promise对象用于表示一个异步操作的最终完成 (或失败), 及其结果值。

    let closeCow = new Promise((resolve, reject) => {
        console.log('把牛放进冰箱');
        resolve();
    });
    closeCow
        .then(open())   // 打开冰箱
        .then(settle()) // 放牛进去
        .then(close()); // 关上冰箱
    

    这就是promise最简单的应用。

    2. promise深入了解

    2.1 Promise状态

    • Pending(待处理): promise初始化的状态,正在运行,既没有完成也没有失败的状态,此状态可以提升为fulfilledrejected状态

    • Fulfilled(已完成): 如果回调函数实现Promise的resolve(回调函数),那么state变为fulfilled

    • Rejected(已拒绝): 如果Promise调用过程中遭到拒绝或发生异常,state就会处于rejected状态。

    • Settled(不变的): Promise从pending状态提升后,状态要么变为fulfilled,要么变为rejected,没有其他情况。

      Promise 的状态一旦改变,就永久保持该状态,不会再变了。

    2.2 promise实例方法

    所谓实例方法(instance method)就是必须实例化之后才能调用的方法,在JS中表现为使用new关键字实例化之后才能调用的,是定义在原型上的方法,即Promise.prototype.methodName;

    • Promise.prototype.then(onFulfilled, onRejected): 用来注册当状态变为fulfilled或者reject时的回调函数

      // onFulfilled 是用来接收promise成功的值
      // onRejected 是用来接收promise失败的原因
      promise.then(onFulfilled, onRejected);
      

      注意,then方法是异步执行的,onFulfilledonRejected方法只会执行其中一个,因为promise状态是单向变化的,要么fulfilled、要么rejected:

      案例一:

      const promise = new Promise((resolve, reject) => {
          resolve('fulfilled...'); // 状态由 pending --> fulfilled
      });
      
      promise.then(res => {
          console.log(res); // 只会调用 onFulfilled
      }, err => {
          console.log(err); // 不会调用 rejected
      })
      // fulfilled
      

      案例二:

      const promise = new Promise((resolve, reject) => {
          reject('rejected...'); // 状态由 pending --> rejected
      });
      
      promise.then(res => {
          console.log(res); // 不会调用 onFulfilled
      }, err => {
          console.log(err); // 只会调用 rejected
      })
      // rejected
      

      案例三:

      const promise1 = new Promise((resolve, reject) => {
          resolve('fulfilled...'); // 状态先由 pending --> rejected
          reject('rejected...'); //  状态不会再变 pending --> rejected
      });
      
      promise1.then(res => {
          console.log(res); // 只会调用 onFulfilled
      }, err => {
          console.log(err); // 不会调用 rejected
      })
      // fulfilled
      
      const promise2 = new Promise((resolve, reject) => {
          reject('rejected...'); // 状态先由 pending --> rejected
          resolve('fulfilled...'); // 状态不会再变 pending --> rejected
      });
      
      promise2.then(res => {
          console.log(res); // 不会调用 onFulfilled
      }, err => {
          console.log(err); // 只会调用 rejected
      })
      // rejected
      

      通过以上三个案例,我们发现,promise状态一旦由pending提升为fulfilled或rejected就不会再改变了,只能单向变化;并且then方法中只会调用其中一个方法(成功的回调或者事变的回调),不会二者都调用。

      Promise.resolve(1)
        .then(2)
        .then(Promise.resolve(3))
        .then(console.log)
      
      // 输出 1
      

      Promise的then方法的参数期望是函数,传入非函数则会发生值穿透

    • Promise.prototype.catch(onRejected): catch在链式写法中可以捕获前面then中发送的异常

      promise.then(res => {
         console.log('haha');
         return 'haha'
      }).then(res => {
          throw new Error('hehe'); // 此处错误被捕获
      }).catch(err => {
          console.log(err)
      })
      
      promise.then(res => {
          console.log('haha');
          throw new Error('haha'); // 此处错误被捕获
      }).then(res => {
          console.log('hehe');
          throw new Error('hehe'); // 此处不会执行
      }).catch(err => {
          console.log(err)
      })
      

      catch一旦捕获到了一个then中的错误,后续的then方法就不会再执行下去了。其实,catch相当于then(null,onRejected),前者只是后者的语法糖而已,使用catch方法替代更好吧。

    • Promise.prototype.finally(onFinally): 无论当前promise的状态是完成(fulfilled)还是失败(rejected)都会执行的回调,并且返回新的promise对象。

      promise.then(res => {
          throw new Error('test');
      }).catch(errr => {
          console.log(err);
      }).finally(()=>{
          // 返回状态为(resolved 或 rejected)都会执行
          console.log('当前promise执行结束')
      });
      
      let isLoading = true;
      fetch(myRequest).then(function(response) {
          let contentType = response.headers.get("content-type");
          if(contentType && contentType.includes("application/json")) {
            return response.json();
          }
          throw new TypeError("Oops, we haven't got JSON!");
        })
        .then(function(json) { /* process your JSON further */ })
        .catch(function(error) { console.log(error); })
        .finally(function() { isLoading = false; });
      
    • Promise.prototype.constructor: 返回被创建的实例函数. 默认为 Promise 函数.

    2.3 promise静态方法

    所谓静态方法(static method)就是可以直接通过对象调用,而不用实例化的方法,即Promise.methodName();

    • Promise.resolve(value):返回一个状态由给定value(可以是普通值,也可以是promise对象)决定的Promise对象。如果value是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定,否则返回成功的promise对象(状态为fulfilled)

    • Promise.reject(reason):返回一个状态为失败(rejected)的Promise对象,并将给定的失败信息传递给对应的处理方法

    • Promise.all(iterable): 一损俱损,只要一个失败,就返回新的pomise对象,否则就等待所有的状态提升为fulfilied。“团结”

      用于处理多个promise对象的状态集合,集合中可以传入常数,当做成功的promise返回值。当集合中一旦有一个promise转改变为rejected,all的状态就变为rejected:

      const p1 = Promise.resolve(1);
      const p2 = Promise.resolve(2);
      const p3 = Promise.reject(3);
      const p4 = Promise.resolve(4);
      // 没有错误
      Promise.all([p1,p2,33, p4]).then(res => {
          console.log(res); // [1,2,33,4]
      })
      // 有错误
      Promise.all([p1,p2,p3, 33, p4]).then(res => {
          console.log(res); // 只要发现一个错误,就不会执行fulfilled方法
      }).catch(err => {
          console.log(err); // 执行错误,输出3
      })
      

      当集合中所有的promise状态都为fulfilled时,必须等待所有的promise执行完毕,即所有的promise对象状态都提升为fulfilled再返回新dePromise对象,执行then方法。

    • Promise.race(iterable): 一荣俱荣,只要一个状态改变(fulfilled或reject),就返回新的promise对象。“赛跑”

      用于处理多个promise对象的状态集合,集合中可以传入常数,当做成功的promise返回值。当集合中一旦有一个promise转改变为fulfilled或rejected,all的状态就提升:

      Promise.race([p1,p2,33,p4]).then(res=> {
          console.log(res); // 只会输出其中一个状态成功改变的,输出:1
      });
      
      Promise.race([p1,p2,p3, 33,p4]).then(res=> {
          console.log(res); // 虽然p3出错了,但是p1状态先提升,只会执行状态最先提升的,输出:1
      }).catch(err => {
          console.log(err);// 这里不会执行
      });
      
    • *Promise.any(iterable): 处于提案中。。。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

      手动实现如下:

      Promise.any = function(arr) {
          var self = this;
          return new Promise(function(resolve, reject){
              var errors = [];
              var count = 0;
              var len = arr.length;
              for(var i=0;i<len;i++){
                  // 只要有一个实例状态变为fulfilled,新的Promise状态就会改变为fulfilled
                  self.resolve(arr[i]).then(function(res){
                      resolve(res);
                  }, function(err){
                      errors[count] = err;
                      count++;
                      // 否则等待所有的rejected,新的Promise状态才会改变为rejected
                      if(count === len){
                          reject(errors)
                      }
                  })
              }
          })
      }
      
    • *Promise.allSettled(iterable): ES2020将实现。。。 只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。不关心结果,只关心有没有执行完毕,该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected

      手动实现如下:

      Promise.allSettled = function(arr){
          var results = [];
          var len = arr.length;
          for(var i=0;i<len;i++){
              this.resolve(arr[i]).then(function(res){
                  results.push({status:'fulfilled', value: res});
              }, function(err){
                  results.push({status:'rejected', value: err});
              })
          }
          // 一旦结束,状态总是`fulfilled`,不会变成`rejected`
          return new Promise(function(resolve, reject) {
             resolve(results)
          })
      }
      
    • *Promise.try():事实上,Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。

      需求的提出:

      不论函数f是同步函数还是异步操作,但是想用 Promise 来处理它:

      Promise.resolve().then(f).catch()
      

      ​ 但是,如果f是同步函数,那么它会在本轮事件循环的末尾执行。

      const f = () => console.log('now');
      Promise.resolve().then(f);
      console.log('next');
      // next
      // now
      

      有没有方法,让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API捕获错误:

      const f = () => console.log('now');
      (
        () => new Promise(
          resolve => resolve(f())
        )
      )();
      console.log('next');
      // now
      // next
      
      // 或者
      (async () => f())();
      console.log('next');
      // now
      // next
      

      Promise.try替代上面的代码:

      const f = () => console.log('now');
      Promise.try(f);
      console.log('next');
      // now
      // next
      

      手动实现如下:

      Promise.try = function(fn){
          if(typeof fn !== 'function') return;
          return new Promise(function(resolve, reject) {
              return resolve(fn());
          })
      }
      

    2.3 promise属性

    • Promise.length:length属性,其值总是1(构造器参数的数目)
    • Promise.prototypePromise构造器的原型

    3. JS事件执行机制(Event Loop)

    为了更好地理解promise的应用,先需要理解JS执行机制。

    运行时概念

    • 函数调用形成了一个栈帧 (call stack)。

    • 对象被分配在一个堆(heap)中,即用以表示一大块非结构化的内存区域。

    • 所有的异步操作都会进入队列(queue)中等待被执行。

      [图片上传失败...(image-11921a-1570631381694)]

    一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都关联着一个用以处理这个消息的函数。

    函数的处理会一直进行到执行栈再次为空为止;然后事件循环将会处理队列中的下一个消息(如果还有的话)。

    事件循环机制

    [图片上传失败...(image-d11d0d-1570631381694)]

    JS执行时,将任务分为同步任务异步任务,同步任务都在主线程上执行(主代码块),形成一个执行栈,异步任务会被加入到任务队列里面。

    任务队列中的任务分为两种任务类型:macrotask和microtask任务队列里面微任务优先于宏任务执行,先执行完任务队列里面所有的微任务,然后再执行任务队列里面的宏任务。

    • 宏任务:script(主代码块), setTimeout, setInterval, setImmediate, I/O, UI rendering,MessageChannel、setImmediate(Node.js 环境)。每次执行栈执行的代码就是一个宏任务

    • 微任务process.nextTick(nodejs相关), Promise, Object.observer, MutationObserver在当前宏任务(主线程) 执行结束后立即执行的任务

      [图片上传失败...(image-6695ba-1570631381694)]

    JS运行机制:

    • 执行栈中的宏任务(栈中没有就从事件队列中获取),一般首先执行普通代码块(script)

    • 执行过程中如果遇到微任务,就将它添加到微任务队列中

    • 当前宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)

    • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染

    • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取),以此循环...

      JS运行机制

    参考:

    4. promise测试题

    4.1 示例一:

    new Promise(resolve => {
    // promise构造函数里面是同步代码区,和普通代码块一样
        console.log(5);
        resolve(1);
        
        Promise.resolve().then(() => {
            console.log(2)
        });
        console.log(4)
    }).then(t => {
        console.log(t)
    }).catch(()=>{
        console.log(6);
    }).finally(() =>{
        console.log(0);
    });
    console.log(3);
    

    <details>
    <summary>输出结果</summary>
    <pre>5 4 3 2 1 0</pre>
    </details>

    4.2 示例二:

    console.log('script start'); // 宏任务1
    
    setTimeout(function() {
      console.log('setTimeout'); // 宏任务2
    }, 0);
    
    Promise.resolve().then(function() { 
      console.log('promise1');// 微任务1
    }).then(function() {
      console.log('promise2'); // 微任务1
    });
    
    console.log('script end'); // 宏任务1
    

    <details>
    <summary>输出结果</summary>
    <pre>
    script start
    script end
    promise1
    promise2
    setTimeout
    </pre>
    </details>

    4.3 示例三:

    let p1 = new Promise((resolve,reject)=>{
      let num = 6
      if(num<5){
        console.log('resolve1')
        resolve(num)
      }else{
        console.log('reject1')
        reject(num)
      }
    })
    p1.then((res)=>{
      console.log('resolve2')
      console.log(res)
    },(rej)=>{
      console.log('reject2')
      let p2 = new Promise((resolve,reject)=>{
        if(rej*2>10){
          console.log('resolve3')
          resolve(rej*2)
        }else{
          console.log('reject3')
          reject(rej*2)
        }
      })
      return p2
    }).then((res)=>{
      console.log('resolve4')
      console.log(res)
    },(rej)=>{
      console.log('reject4')
      console.log(rej)
    })
    

    <details>
    <summary>输出结果</summary>
    <pre>
    reject1
    reject2
    resolve3
    resolve4
    12
    </pre>
    </details>

    4.4 示例四:

    Tasks, microtasks, queues and schedules

    <iframe height="302" style="width: 100%;" scrolling="no" title="ES6-promise-demo3" src="https://codepen.io/keekuun/embed/abbzjbM?height=302&theme-id=0&default-tab=js,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
    See the Pen <a href='https://codepen.io/keekuun/pen/abbzjbM'>ES6-promise-demo3</a> by Keekuun
    (<a href='https://codepen.io/keekuun'>@keekuun</a>) on <a href='https://codepen.io'>CodePen</a>.
    </iframe>

    <details>
    <summary>点击内部输出</summary>
    <pre>
    click
    promise
    mutate
    click
    promise
    mutate
    timeout
    timeout
    </pre>
    </details>

    4.5 示例五:

    从event loop到async await来了解事件循环机制

    async function a1 () { // async关键字
        console.log('a1 start')
        await a2() // await关键字
        console.log('a1 end')
    }
    async function a2 () {
        console.log('a2')
    }
    
    console.log('script start')
    
    setTimeout(() => {
        console.log('setTimeout')
    }, 0)
    
    Promise.resolve().then(() => {
        console.log('promise1')
    })
    
    a1()
    
    let promise2 = new Promise((resolve) => {
        resolve('promise2.then')
        console.log('promise2')
    })
    
    promise2.then((res) => {
        console.log(res)
        Promise.resolve().then(() => {
            console.log('promise3')
        })
    })
    console.log('script end')
    

    <details>
    <summary>输出结果</summary>
    <pre>
    script start
    a1 start
    a2
    promise2
    script end
    promise1
    a1 end
    promise2.then
    promise3
    setTimeout
    </pre>
    </details>

    4.6 示例五:

    async function test() { 
        console.log('test start'); 
        await undefined; 
        console.log('await 1'); 
        await new Promise(resolve => {  
            console.log('promise in async'); 
            resolve(); 
        }); 
        console.log('await 2'); 
    } 
     
    test(); 
    new Promise((resolve) => { 
        console.log('promise'); 
        resolve(); 
    }) 
    .then(() => {console.log(1)}) 
    .then(() => {console.log(2)}) 
    .then(() => {console.log(3)}) 
    .then(() => {console.log(4)});
    

    <details>
    <summary>输出结果</summary>
    <pre>
    test start
    promise
    await 1
    promise in async
    1
    await 2
    2
    3
    4
    </pre>
    </details>

    5. 手写promise

    • 使用ES5之前的语法实现:
    (function () {
        // 判断function
        function isFunction(fn) {
            return typeof fn === 'function';
        }
    
        // 状态 pending、fulfilled、rejected
        var PENDING = 'pending';
        var FULFILLED = 'fulfilled';
        var REJECTED = 'rejected';
    
        // 构造方法
        var Kromise = function (handle) {
            // 当前状态
            this._status = PENDING;
            // 添加成功回调队列
            this._fulfilledQueue = [];
            // 添加失败回调队列
            this._rejectedQueue = [];
            // 引用当前this对象
            var self = this;
    
            if (!isFunction(handle)) {
                throw new Error('Parameter handle is not a function!')
            }
    
            // 添加resolve时执行的函数
            function _resolve(val) {
                var run = function () {
                    if (self._status !== PENDING) return;
                    // 依次执行成功队列中的函数,并清空队列
                    var runFulfilled = function (res) {
                        var resolve;
                        while (resolve = self._fulfilledQueue.shift()) { // 出栈
                            resolve(res);
                        }
                    };
    
                    // 依次执行失败队列中的函数,并清空队列
                    var runRejected = function (err) {
                        var reject;
                        while (reject = self._rejectedQueue.shift()) { // 出栈
                            reject(err);
                        }
                    };
                    /* 如果resolve的参数为Kromise对象,则必须等待该Kromise对象状态改变后,
                     * 当前Kromise的状态才会改变,且状态取决于参数Kromise对象的状态
                     */
                    if (val instanceof Kromise) {
                        val.then(function (value) {
                            self._status = FULFILLED;
                            self._value = value;
                            runFulfilled(value)
                        }, function (err) {
                            self._status = REJECTED;
                            self._value = err;
                            runRejected(err);
                        })
                    } else {
                        self._status = FULFILLED;
                        self._value = val;
                        runFulfilled(val);
                    }
    
                };
                // 为了支持同步的Promise,这里采用异步调用
                setTimeout(run, 0)
            }
    
            // 添加reject时执行的函数
            function _reject(err) {
                var run = function () {
                    if (self._status !== PENDING) return;
                    // 依次执行成功队列中的函数,并清空队列
                    self._status = REJECTED;
                    self._value = err;
                    var reject;
                    while (reject = self._fulfilledQueue.shift()) { // 出栈
                        reject(err);
                    }
                };
                // 为了支持同步的Promise,这里采用异步调用
                setTimeout(run, 0)
            }
    
            // 执行handle,捕获异常
            try {
                handle(_resolve.bind(this), _reject.bind(this));
            } catch (e) {
                _reject(e);
            }
        };
    
        // 属性
        Kromise.length = 1;
    
        // 实例方法
        // 实现then方法
        Kromise.prototype.then = function (onFulfilled, onRejected) {
            var self = this;
            // 返回一个新的Kromise对象
            return new Kromise(function (onFulfilledNext, onRejectedNext) {
                // 成功时的回调
                var fulfilled = function (val) {
                    try {
                        // 如果不是函数,值穿透
                        if (!isFunction(onFulfilled)) {
                            onFulfilledNext(val)
                        } else {
                            var res = onFulfilled(val);
                            // 如果当前回调函数返回Kromise对象,必须等待其状态改变后在执行下一个回调
                            if (res instanceof Kromise) {
                                res.then(onFulfilledNext, onRejectedNext);
                            } else {
                                //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                                onFulfilledNext(res);
                            }
                        }
                    } catch (e) {
                        // 如果函数执行出错,新的Kromise对象的状态为失败
                        onRejectedNext(e);
                    }
                };
                // 失败时的回调
                var rejected = function (err) {
                    try {
                        if (!isFunction(onRejected)) {
                            onRejectedNext(err)
                        } else {
                            var res = onRejected(err);
                            if (res instanceof Kromise) {
                                res.then(onFulfilledNext, onRejectedNext);
                            } else {
                                onFulfilledNext(res);
                            }
                        }
                    } catch (e) {
                        onRejectedNext(e)
                    }
                };
    
                switch (self._status) {
                    // 当状态为pending时,将then方法回调函数加入执行队列等待执行
                    case PENDING:
                        self._fulfilledQueue.push(fulfilled);
                        self._rejectedQueue.push(rejected);
                        break;
                    // 当状态已经改变时,立即执行对应的回调函数
                    case FULFILLED:
                        fulfilled(self._value);
                        break;
                    case REJECTED:
                        rejected(self._value);
                        break;
                }
            });
        };
    
        // 实现catch方法
        Kromise.prototype.catch = function (onRejected) {
            return this.then(undefined, onRejected);
        };
    
        // 实现finally方法
        Kromise.prototype.finally = function (onFinally) {
            return this.then(function (value) {
                Kromise.resolve(onFinally()).then(function () {
                    return value;
                })
            }, function (err) {
                Kromise.resolve(onFinally()).then(function () {
                    throw new Error(err);
                })
            })
        };
    
        // 静态方法
        // 实现resolve方法
        Kromise.resolve = function (value) {
            // 如果参数是Kromise实例,直接返回这个实例
            if (value instanceof Kromise) {
                return value;
            }
            return new Kromise(function (resolve) {
                resolve(value)
            })
        };
        // 实现reject方法
        Kromise.reject = function (value) {
            return new Kromise(function (resolve, reject) {
                reject(value)
            })
        };
        // 实现all方法
        Kromise.all = function (arr) {
            var self = this;
            return new Kromise(function (resolve, reject) {
                var values = [];
                for (var i = 0, len = arr.length; i < len; i++) {
                    // 数组参数如果不是Kromise实例,先调用Kromise.resolve
                    self.resolve(arr[i]).then(function (res) {
                        values.push(res);
                        // 所有状态都变成fulfilled时返回的Kromise状态就变成fulfilled
                        if (values.length === arr.length) {
                            resolve(values);
                        }
                    }, function (e) {
                        // 有一个被rejected时返回的Kromise状态就变成rejected
                        reject(e);
                    })
                }
            })
        };
    
        // 实现race方法
        Kromise.race = function (arr) {
            var self = this;
            return new Kromise(function (resolve, reject) {
                for (var i = 0, len = arr.length; i < len; i++) {
                    // 只要有一个实例率先改变状态,新的Kromise的状态就跟着改变
                    self.resolve(arr[i]).then(function (res) {
                        resolve(res);
                    }, function (err) {
                        reject(err);
                    })
                }
            })
        };
        // 实现any方法
        Kromise.any = function (arr) {
            var self = this;
            return new Kromise(function (resolve, reject) {
                var count = 0;
                var errors = [];
                for (var i = 0, len = arr.length; i < len; i++) {
                    // 只要有一个实例状态变为fulfilled,新的Kromise状态就会改变为fulfilled
                    self.resolve(arr[i]).then(function (res) {
                        resolve(res);
                    }, function (err) {
                        errors[count] = err;
                        count++;
                        // 否则等待所有的rejected,新的Kromise状态才会改变为rejected
                        if (count === arr.length) {
                            reject(errors);
                        }
                    })
                }
            })
    
        };
        // 实现allSettled方法
        Kromise.allSettled = function (arr) {
            var results = [];
            var len = arr.length;
            for (var i = 0; i < len; i++) {
                this.resolve(arr[i]).then(function (res) {
                    results.push({status: FULFILLED, value: res});
                }, function (err) {
                    results.push({status: REJECTED, value: err});
                })
            }
            // 一旦结束,状态总是`fulfilled`,不会变成`rejected`
            return new Kromise(function (resolve, reject) {
                resolve(results)
            })
        };
        // 实现try方法
        Kromise.try = function (fn) {
            if (!isFunction(fn)) return;
            return new Kromise(function (resolve, reject) {
                return resolve(fn());
            })
        };
    
        // 挂载
        window.Kromise = Kromise;
    })();
    
    
    • 使用ES6 class语法实现
      // 判断变量否为function
      const isFunction = variable => typeof variable === 'function'
      // 定义Promise的三种状态常量
      const PENDING = 'PENDING'
      const FULFILLED = 'FULFILLED'
      const REJECTED = 'REJECTED'
    
      class MyPromise {
        constructor (handle) {
          if (!isFunction(handle)) {
            throw new Error('MyPromise must accept a function as a parameter')
          }
          // 添加状态
          this._status = PENDING
          // 添加状态
          this._value = undefined
          // 添加成功回调函数队列
          this._fulfilledQueues = []
          // 添加失败回调函数队列
          this._rejectedQueues = []
          // 执行handle
          try {
            handle(this._resolve.bind(this), this._reject.bind(this)) 
          } catch (err) {
            this._reject(err)
          }
        }
        // 添加resovle时执行的函数
        _resolve (val) {
          const run = () => {
            if (this._status !== PENDING) return
            // 依次执行成功队列中的函数,并清空队列
            const runFulfilled = (value) => {
              let cb;
              while (cb = this._fulfilledQueues.shift()) {
                cb(value)
              }
            }
            // 依次执行失败队列中的函数,并清空队列
            const runRejected = (error) => {
              let cb;
              while (cb = this._rejectedQueues.shift()) {
                cb(error)
              }
            }
            /* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
              当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
            */
            if (val instanceof MyPromise) {
              val.then(value => {
                this._value = value
                this._status = FULFILLED
                runFulfilled(value)
              }, err => {
                this._value = err
                this._status = REJECTED
                runRejected(err)
              })
            } else {
              this._value = val
              this._status = FULFILLED
              runFulfilled(val)
            }
          }
          // 为了支持同步的Promise,这里采用异步调用
          setTimeout(run, 0)
        }
        // 添加reject时执行的函数
        _reject (err) { 
          if (this._status !== PENDING) return
          // 依次执行失败队列中的函数,并清空队列
          const run = () => {
            this._status = REJECTED
            this._value = err
            let cb;
            while (cb = this._rejectedQueues.shift()) {
              cb(err)
            }
          }
          // 为了支持同步的Promise,这里采用异步调用
          setTimeout(run, 0)
        }
        // 添加then方法
        then (onFulfilled, onRejected) {
          const { _value, _status } = this
          // 返回一个新的Promise对象
          return new MyPromise((onFulfilledNext, onRejectedNext) => {
            // 封装一个成功时执行的函数
            let fulfilled = value => {
              try {
                if (!isFunction(onFulfilled)) {
                  onFulfilledNext(value)
                } else {
                  let res =  onFulfilled(value);
                  if (res instanceof MyPromise) {
                    // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                    res.then(onFulfilledNext, onRejectedNext)
                  } else {
                    //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                    onFulfilledNext(res)
                  }
                }
              } catch (err) {
                // 如果函数执行出错,新的Promise对象的状态为失败
                onRejectedNext(err)
              }
            }
            // 封装一个失败时执行的函数
            let rejected = error => {
              try {
                if (!isFunction(onRejected)) {
                  onRejectedNext(error)
                } else {
                    let res = onRejected(error);
                    if (res instanceof MyPromise) {
                      // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                      res.then(onFulfilledNext, onRejectedNext)
                    } else {
                      //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                      onFulfilledNext(res)
                    }
                }
              } catch (err) {
                // 如果函数执行出错,新的Promise对象的状态为失败
                onRejectedNext(err)
              }
            }
            switch (_status) {
              // 当状态为pending时,将then方法回调函数加入执行队列等待执行
              case PENDING:
                this._fulfilledQueues.push(fulfilled)
                this._rejectedQueues.push(rejected)
                break
              // 当状态已经改变时,立即执行对应的回调函数
              case FULFILLED:
                fulfilled(_value)
                break
              case REJECTED:
                rejected(_value)
                break
            }
          })
        }
        // 添加catch方法
        catch (onRejected) {
          return this.then(undefined, onRejected)
        }
        // 添加静态resolve方法
        static resolve (value) {
          // 如果参数是MyPromise实例,直接返回这个实例
          if (value instanceof MyPromise) return value
          return new MyPromise(resolve => resolve(value))
        }
        // 添加静态reject方法
        static reject (value) {
          return new MyPromise((resolve ,reject) => reject(value))
        }
        // 添加静态all方法
        static all (list) {
          return new MyPromise((resolve, reject) => {
            /**
             * 返回值的集合
             */
            let values = []
            let count = 0
            for (let [i, p] of list.entries()) {
              // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
              this.resolve(p).then(res => {
                values[i] = res
                count++
                // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
                if (count === list.length) resolve(values)
              }, err => {
                // 有一个被rejected时返回的MyPromise状态就变成rejected
                reject(err)
              })
            }
          })
        }
        // 添加静态race方法
        static race (list) {
          return new MyPromise((resolve, reject) => {
            for (let p of list) {
              // 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
              this.resolve(p).then(res => {
                resolve(res)
              }, err => {
                reject(err)
              })
            }
          })
        }
        finally (cb) {
          return this.then(
            value  => MyPromise.resolve(cb()).then(() => value),
            reason => MyPromise.resolve(cb()).then(() => { throw reason })
          );
        }
      }
    

    相关文章

      网友评论

        本文标题:ES6-promise学习及手写promise

        本文链接:https://www.haomeiwen.com/subject/vnpgpctx.html