JS专题系列之Promise的原理及实现

一、Promise概念

Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

promise解决的问题

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  • promise可以支持多个并发的请求,获取并发请求中的数据
  • 这个promise可以解决异步的问题,本身不能说promise是异步的

二、分析Promise

如果我们想要封装promise就需要考虑以下几个问题

  • 如何让Promise变成一个微任务
  • 如何管理Promise的状态
  • then方法的返回值问题
  • 静态方法: resolverejectallrace

三、基于ES6实现AlleyPromise

Promise的构造函数必须接受一个函数参数(也就是需要执行异步任务的函数),这个函数传入后立即调用,该函数有2个参数也就说resolvereject,如果我们在调用Promise的时候没有传入参数则会抛出异常

new Promise(); // Uncaught TypeError: Promise resolver undefined is not a function

另外Promise是有三个状态 pending(等待态),fulfiled(成功态),rejected(失败态),因此我们需要在初始状态保持PENDING状态,调用resolve的时候保持FULFILED状态,调用reject的时候保持REJECTED状态

我们要注意的是如果Promise的状态更改过一次后就不会再次进行更改,例如先调用了resolve然后又调用了reject那么状态只会保持为FULFILED状态而非REJECTED状态

1、根据以上已知条件我们就可以进行第一步的封装

class AlleyPromise{
    // 1、Promise三种状态
    static PENDING = 'PENDING';
    static FULFILED = 'FULFILED';
    static REJECTED = 'REJECTED';

    public value:any;
    private promiseStatus:string;


    constructor(callback){
        // 2、容错处理
        if(typeof callback !== 'function'){
            throw new TypeError('Promise resolver undefined is not a function')
        }

        //3、初始状态
        this.promiseStatus = AlleyPromise.PENDING;

        //4、定义初始值
        this.value;

        //5、调用callback函数
        callback(this._resolve.bind(this),this._reject.bind(this))
    }
   private _resolve(){
        // 6、更改成功状态
        if(this.promiseStatus !== AlleyPromise.PENDING) return;
        this.promiseStatus = AlleyPromise.FULFILED;
    }
    private _reject(){
        // 7、更改失败状态
        if(this.promiseStatus !== AlleyPromise.PENDING) return;
        this.promiseStatus = AlleyPromise.REJECTED;
    }
}

接下来我们就需要处理的事情就是then方法的封装

  • then方法接收2个参数,一个是成功的回调,一个是失败的回调
  • 每个回调中会有相对应的参数进行传递
  • resolve||reject函数调用完成后在执行then函数中的回调

条件1与条件2相对比较简单,条件3我们如何管理? 其实很简单,then函数在执行的时候我们将then函数中的回调注册到一个队列中去,当resolve || reject 函数调用的时候我们在从队列中读取执行即可

this.resolveQueue = [];
this.rejectQueue = [];

_resolve(val){
  // 更改成功状态
  if(this.promiseStatus !== AlleyPromise.PENDING) return;
  this.promiseStatus = AlleyPromise.FULFILED;
    this.value = val;
  let handler;
  while(handler = this.resolveQueues.shift()){
    handler(this.value)
  }
}

then(resolveHandler,rejectHandler) {
   this.resolveQueues.push(resolveHandler)
   this.rejectQueues.push(rejectHandler)
}

2、根据以上已知条件我们就可以进行第一步的封装

class AlleyPromise{
    // 1、Promise三种状态
    static PENDING = 'PENDING';
    static FULFILED = 'FULFILED';
    static REJECTED = 'REJECTED';

    public value:any;

    private promiseStatus:string;
    private resolveQueues:Function[];
    private rejectQueues:Function[];

    constructor(callback){
        // 容错处理
        if(typeof callback !== 'function'){
            throw new TypeError('Promise resolver undefined is not a function')
        }

        // 初始状态
        this.promiseStatus = AlleyPromise.PENDING;
        
        // 定义resolve函数队列 reject函数队列
        this.resolveQueues = [];
        this.rejectQueues = [];

        //定义初始值
        this.value;

        //调用callback函数
        callback(this._resolve.bind(this),this._reject.bind(this))
    }
   private _resolve(val){
        // 更改成功状态
        if(this.promiseStatus !== AlleyPromise.PENDING) return;
        this.promiseStatus = AlleyPromise.FULFILED;
                this.value = val;
        let handler;
        while(handler = this.resolveQueues.shift()){
            handler(this.value )
        }
    }
    private _reject(){
        // 更改失败状态
        if(this.promiseStatus !== AlleyPromise.PENDING) return;
        this.promiseStatus = AlleyPromise.REJECTED;
        this.value = val;
        let handler;
        while(handler = this.rejectQueues.shift()){
            handler(this.value)
        }
    }
    public then(resolveHandler,rejectHandler) {
        this.resolveQueues.push(resolveHandler)
        this.rejectQueues.push(rejectHandler)
    }
}

截止到目前为止我们就封装好了一个简易版的promise但是依旧有很多问题,我们进行逐一排查

  • 当promise回调函数中的代码逻辑不是异步的时候,我们会发现then方法不会被调用
  • promise可以进行链式调用,依旧是说then方法的返回值应该是一个promise

错误演示

new AlleyPromise((resolve,reject)=>{
    resolve("执行")
}).then((val)=>{
    console.log(val); // 这里不会进行console,原因是resolve调用完毕后才会调用then,事件队列中的函数根本没有push进去
})

解决方案也很简单,我们只需要保证我们代码块中的_resolve && _reject是异步的就可以了,这样等then方法执行完毕后,_resolve && _reject才会去执行

但是需要注意的是因为promise是微任务,所以我们不能使用setTimeout,这里面我们可以使用postMessage

_resolve(val){
  window.addEventListener('message',()=>{
    // 更改成功状态
    if(this.promiseStatus !== AlleyPromise.PENDING) return;
    this.promiseStatus = AlleyPromise.FULFILED;
    this.value = val;
    let handler;
    while(handler = this.resolveQueues.shift()){
      handler(this.value)
    }
  })
  window.postMessage('')
}

_reject(val){
  window.addEventListener('message',()=>{
    // 更改失败状态
    if(this.promiseStatus !== AlleyPromise.PENDING) return;
    this.promiseStatus = AlleyPromise.REJECTED;
    this.value = val;
    let handler;
    while(handler = this.rejectQueues.shift()){
      handler(this.value)
    }
  })
  window.postMessage('')
}

解决完问题一后完们来解决问题二Promise链式调用的问题,说到链式调用我们肯定知道只需要在then方法中返回一个promise即可,但是这样真的可以吗?我们来做下测试

class AlleyPromise{
    // 1、Promise三种状态
    static PENDING = 'PENDING';
    static FULFILED = 'FULFILED';
    static REJECTED = 'REJECTED';

    constructor(callback){
        // 容错处理
        if(typeof callback !== 'function'){
            throw new TypeError('Promise resolver undefined is not a function')
        }

        // 初始状态
        this.promiseStatus = AlleyPromise.PENDING;
        
        // 定义resolve函数队列 reject函数队列
        this.resolveQueues = [];
        this.rejectQueues = [];

        //定义初始值
        this.value;

        //调用callback函数
        callback(this._resolve.bind(this),this._reject.bind(this))
    }
    _resolve(val){
        window.addEventListener('message',()=>{
            // 更改成功状态
            if(this.promiseStatus !== AlleyPromise.PENDING) return;
            this.promiseStatus = AlleyPromise.FULFILED;
            this.value = val;
            let handler;
            while(handler = this.resolveQueues.shift()){
                handler(this.value)
            }
        })
        window.postMessage('')
    }
     _reject(val){
        window.addEventListener('message',()=>{
            // 更改失败状态
            if(this.promiseStatus !== AlleyPromise.PENDING) return;
            this.promiseStatus = AlleyPromise.REJECTED;
            this.value = val;
            let handler;
            while(handler = this.rejectQueues.shift()){
                handler(this.value)
            }
        })
        window.postMessage('')
    }
    then(resolveHandler,rejectHandler) {
        this.resolveQueues.push(resolveHandler)
        this.rejectQueues.push(rejectHandler)

        return new AlleyPromise((resolve,reject)=>{
            resolve()
        })
    }
}



// 测试
new AlleyPromise((resolve,reject)=>{
        setTimeout(() => {
            resolve()
        }, 10);
}).then(()=>{
  console.log('then1'); // 后输出
}).then(()=>{
  console.log('then2'); // 先输出
})

上面代码中我们会发现then2 先输出 then1后输出,原因也很简单因为我们then方法返回出去的那个promise回调中的代码是同步的,它会优先执行,而then1中的代码还在事件队列中,而我们的事件队列是一个异步的方法

解决方案: 其实我们只需要将return new AlleyPromise((resolve,reject)=>{ resolve() })中的resolve也加入到事件队列中即可

then(resolveHandler,rejectHandler) {   
  return new AlleyPromise((resolve,reject)=>{
    function newResolveHandler(){
      resolveHandler();
      resolve();
    }

    function newRejectHandler(){
      rejectHandler();
      reject();
    }


    this.resolveQueues.push(newResolveHandler)
    this.rejectQueues.push(newRejectHandler)
  })
}

// 测试
new AlleyPromise((resolve,reject)=>{
        setTimeout(() => {
            resolve()
        }, 10);
}).then(()=>{
  console.log('then1'); // 先输出
}).then(()=>{
  console.log('then2'); // 后输出
})

边界处理

这样我们的then方法就封装完毕了吗?其实不是的还有好多边界情况需要处理,例如以下问题

  • then方法中的回调函数接收不到参数
  • 如果我们在调用then方法的时候return出去一个值后后面的then方法能接收到这个值吗?

错误演示

new AlleyPromise((resolve,reject)=>{
  setTimeout(() => {
    resolve(123)
  }, 10);
}).then((val)=>{
  console.log('then1',val);// then1 undefined
  return '456'
}).then((val)=>{
  console.log('then2',val)// then2 undefined
})

错误一的解决很简单newResolveHandler函数其实就是我们存入事件队列中的函数,事件队列中的函数会接收到传递的值

then(resolveHandler,rejectHandler) {     
  return new AlleyPromise((resolve,reject)=>{
    function newResolveHandler(val){
      resolveHandler(val);
      resolve();
    }

    function newRejectHandler(val){
      rejectHandler(val);
      reject();
    }


    this.resolveQueues.push(newResolveHandler)
    this.rejectQueues.push(newRejectHandler)
  })
}

// 测试
new AlleyPromise((resolve,reject)=>{
  setTimeout(() => {
    resolve(123)
  }, 10);
}).then((val)=>{
  console.log('then1',val);// then1 123
  return '456'
}).then((val)=>{
  console.log('then2',val)// then2 undefined
})

错误二我们需要加一些边界判断来完成

    then(resolveHandler,rejectHandler) {  
        return new AlleyPromise((resolve,reject)=>{
           function newResolveHandler(val){
               // 首先判断 resolveHandler是否是一个函数
               if(typeof resolveHandler === 'function') {
                   /*
                    获取resolveHandler 函数的返回值进行判断
                    如果是promise则继续.then,不是则直接将结果返回
                    */
                    let result = resolveHandler(val);
                    if(result instanceof AlleyPromise){
                        result.then(resolve,reject)
                    } else{
                        resolve(result);
                    }
                    
               } else {
                   resolve(val);
               }
                
           }

           function newRejectHandler(val){
               if(typeof rejectHandler === 'function') {
                    let result  =  rejectHandler(val);
                    if(result instanceof AlleyPromise){
                        result.then(resolve,reject)
                    }else{
                        reject(result);
                    }
                
               } else {
                    reject(val);
               }
                
            }


            this.resolveQueues.push(newResolveHandler)
            this.rejectQueues.push(newRejectHandler)
        })
    }

3、catch方法封装

catch方法封装也挺简单其实也就是then方法第一个参数不需要传递即可

catch(rejectHandler){
  return this.then(undefined,rejectHandler)
}

4、静态方法all方法封装

all方法封装就比较简单了,all函数接收一个Promise[]类型的数组,需要保证这个数组中所有的promise都执行成功后执行resolve返回所有的值

static all(iterator){
  let len = iterator.length;
  let n = 0;
  let vals = [];
  return new AlleyPromise((resolve,reject)=>{
    iterator.forEach((item)=>{
      item.then((val)=>{
        ++n;
        vals.push(val);
        if(len === n) {
          resolve(vals);
        }

      }).catch((e)=>{
        reject(e);
      })
    })
  }

5、静态方法race封装

race封装其实和all一样,只不过race是只要数组中的Promise只要有一个完成的就会调用resolve方法,不需要统计个数

static race(iterator){
        return new AlleyPromise((resolve,reject)=>{
            iterator.forEach((item)=>{
                item.then((val)=>{
                    resolve(val);
                }).catch((e)=>{
                    reject(e);
                })
            })
        })
    }

6、静态方法resolve封装

static resolve(val){
  return new AlleyPromise((resolve)=>{
    resolve(val)
  })
}

7、静态方法reject封装

static reject(val){
  return new AlleyPromise((resolve,reject)=>{
    reject(val)
  })
}

最后完整版封装

class AlleyPromise{
    // 1、Promise三种状态
    static PENDING = 'PENDING';
    static FULFILED = 'FULFILED';
    static REJECTED = 'REJECTED';

    constructor(callback){
        // 容错处理
        if(typeof callback !== 'function'){
            throw new TypeError('Promise resolver undefined is not a function')
        }

        // 初始状态
        this.promiseStatus = AlleyPromise.PENDING;
        
        // 定义resolve函数队列 reject函数队列
        this.resolveQueues = [];
        this.rejectQueues = [];

        //定义初始值
        this.value;

        //调用callback函数
        callback(this._resolve.bind(this),this._reject.bind(this))
    }
    _resolve(val){
        window.addEventListener('message',()=>{
            // 更改成功状态
            if(this.promiseStatus !== AlleyPromise.PENDING) return;
            this.promiseStatus = AlleyPromise.FULFILED;
            this.value = val;
            let handler;
            while(handler = this.resolveQueues.shift()){
                handler(this.value)
            }
        })
        window.postMessage('')
    }
     _reject(val){
        window.addEventListener('message',()=>{
            // 更改失败状态
            if(this.promiseStatus !== AlleyPromise.PENDING) return;
            this.promiseStatus = AlleyPromise.REJECTED;
            this.value = val;
            let handler;
            while(handler = this.rejectQueues.shift()){
                handler(this.value)
            }
        })
        window.postMessage('')
    }
    then(resolveHandler,rejectHandler) {  
        return new AlleyPromise((resolve,reject)=>{
           function newResolveHandler(val){
               // 首先判断 resolveHandler是否是一个函数
               if(typeof resolveHandler === 'function') {
                   /*
                    获取resolveHandler 函数的返回值进行判断
                    如果是promise则继续.then,不是则直接将结果返回
                    */
                    let result = resolveHandler(val);
                    if(result instanceof AlleyPromise){
                        result.then(resolve,reject)
                    } else{
                        resolve(result);
                    }
                    
               } else {
                   resolve(val);
               }
                
           }

           function newRejectHandler(val){
               if(typeof rejectHandler === 'function') {
                    let result  =  rejectHandler(val);
                    if(result instanceof AlleyPromise){
                        result.then(resolve,reject)
                    }else{
                        reject(result);
                    }
                
               } else {
                    reject(val);
               }
                
            }


            this.resolveQueues.push(newResolveHandler)
            this.rejectQueues.push(newRejectHandler)
        })
    }
    catch(rejectHandler){
        return this.then(undefined,rejectHandler)
    }
    static all(iterator){
        let len = iterator.length;
        let n = 0;
        let vals = [];
        return new AlleyPromise((resolve,reject)=>{
            iterator.forEach((item)=>{
                item.then((val)=>{
                    ++n;
                    vals.push(val);
                    if(len === n) {
                        resolve(vals);
                    }

                }).catch((e)=>{
                    reject(e);
                })
            })
        })
    }
    static race(iterator){
        return new AlleyPromise((resolve,reject)=>{
            iterator.forEach((item)=>{
                item.then((val)=>{
                    resolve(val);
                }).catch((e)=>{
                    reject(e);
                })
            })
        })
    }
    static resolve(val){
        return new AlleyPromise((resolve)=>{
            resolve(val)
        })
    }
    static reject(val){
        return new AlleyPromise((resolve,reject)=>{
            reject(val)
        })
    }
}

你可能感兴趣的:(JS专题系列之Promise的原理及实现)