PromiseA+ 规范与实现

124 阅读12分钟

PromiseA+规范和应用

技术参考:promisesaplus.com/

在了解和实现promise规范和应用之前先了解下promise术语,以便后续提到的时候可以有明确统一的概念。

术语

  1. promise是一个有then方法的对象或者函数,其行为都应遵循本规范。
  2. thenable 是一个有then方法的对象和函数。
  3. value是promise状态成功时候的值,也就是resolve的参数,包括各种数据类型,也包括undefined、thenable,甚至是promise。
  4. reason是promise状态失败时候的值,也就是reject的参数,表示拒绝的原因。
  5. exception是一个使用throw抛出的异常值。

规范

接下来分几部分来讲解PromiseA+规范

Promise status

Promise应该有三个状态,注意他们之间的流转。

  1. pending

    • 初始状态,可改变。
    • 一个Promise在resolve和reject之前都处于这个状态。
    • 可以通过resolve => fulfilled 状态。
    • 可以通过reject => rejected 状态。
  2. fulfilled

    • 最终态,不可修改。
    • 一个Promise被resolve之后变成这个状态。
    • 必须拥有一个value值。
  3. rejected

    • 最终态,不可修改。
    • 一个Promise被rejecte之后变成这个状态。
    • 必须拥有一个reason值。 Tips:总结一下Promise的流转是这样的
pending => resolve(value) => fulfilled
pending => rejecet(value) => rejected

then

Promise 应该提供一个.then方法,用来访问最终结果,无论是value还是reason。

  Promise.then( onFulfilled , onRejected )
  1. 参数要求

    • onFulfilled必须是函数类型,如果不是函数,应该被忽略。
    • onRejected 必须是函数类型,如果不是函数,应该被忽略。
  2. onFulfilled

    • 在Promise变成fulfilled状态的时候,应该执行onFulfilled函数,参数是value
    • 在Promise变成fulfilled之前,onFulfilled不应该被执行。
    • 只能调用一次(所以在实现的时候需要一个变量来控制执行次数)
  3. onRejected

    • 在Promise变成rejected状态的时候,应该执行onRejected函数,参数是reason
    • 在Promise变成rejected之前,onRejected不应该被执行。
    • 只能调用一次(所以在实现的时候需要一个变量来控制执行次数)
  4. onFulfilled和onRejected 应该是微任务,我们这里用queueMicrotask来实现微任务的调用

  5. then方法可以被调用多次

    • Promise在变成fulfilled状态后,所有的onFulfilled回调都应该需要按照then的执行顺序,也就是按照注册的顺序执行(所以在实现的时候需要定义一个数组来存放多个onFulfilled的回调)
    • Promise在变成rejected状态后,所有的onRejected回调都应该需要按照then的执行顺序,也就是按照注册的顺序执行(所以在实现的时候需要定义一个数组来存放多个onRejected的回调)
  6. 返回值:then方法都应该返回一个Promise =>这里定义为 promise2

    • onFulfilled和onRejected的执行结果为X,执行resolvePeomise(这里可能会有些难理解,可以先保留,下面会详细讲一下resolvePromise是什么。)
    • onFulfilled或者onRejeted在执行时抛出异常e,promise2都需要被reject。
    • 如果onFulfilled不是一个函数,promise2应该以promise1的value触发onFulfilled。
    • 如果onRejected不是一个函数,promise2应该以promise1的reason触发onRejected。
  7. resolvePromise

    resolvePromise(Promise2, x, resolve, reject)
    
    • 如果Promise2 和x相等,那个reject TypeError

    • 如果x是一个Promise:

      (1) 如果x是pending 状态,那么Promise必须要在pending状态,知道状态修改为fulfilled或者rejected。

      (2) 如果x是fulfilled状态,fulfill Promise the same value。

      (3) 如果x是rejected状态,reject Promise the same reason。

    • 如果x是一个object或者是一个function

      let then = x.then

      如果x.then这步出错,那么reject Promise the same reason。

      如果then是一个函数,then.call(x,resolvePromiseFn,rejectPromiseFn)

      resolvePromiseFn 的入参是y,执行resolvePromise(promise2, y, resolve, rejsect)

      resolvePromise 的入参是r,reject promise with r。

      如果rejectPromise 和 rejectPromise 都调用了那么第一个调用的优先,后面的调用忽略。

      如果调用then方法抛出异常e:如果resolvePromise或者rejectPromise已经被调用,那么忽略,否则以e为reason拒绝Promise。

      如果then不是一个function,以x为value执行(fulfill)执行promise

PromiseA+ 的实现

接下来我们用class实现一下promise

  1. 先定义一个class框架,以及构造函数constructor
class MPromise {
    constructor() {}
}
  1. 定义三种状态类型
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
  1. 设置初始状态
 class MPromise {
    constructor() {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
    }
}
  1. 定义resolve和reject 方法

    • 根据上面的规范, 这两个方法是要更改status的,从pending到fulfilled/rejected
    • 注意这两个函数的入参分别是value和reason
class MPromise {
    constructor() {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
    }

    resolve(value) {  //入参为value
       //当状态为pending时,修改状态为fulfilled,并修改value值
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }

    reject(reason) {
       //当状态为pending时,修改状态为rejected,并修改reason值
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
}
  1. 回想一下new Promise((resolve, reject) => {}) 一般会传入一个函数,我们的这里也要添加一个入参fn
    • 入参是一个函数,并接收resolve和reject两个参数。
    • 注意初始化Promise的时候,就要执行这个函数,并且任何报错都要通过reject刨除去。Tips:这里是同步执行
class MPromise {
    constructor(fn) {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;

        try {
            //这里将this指向为当前this,不希望在执行的时候改变this指向
            fn(this.resolve.bind(this), this.reject.bind(this));
        } catch (e) {
            this.reject(e);
        }
    }
    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
}
  1. 接下来实现一下关键字then方法
    • then方法接收两个参数,onFulfilled和onRejected
    then(onFulfilled, onRejected){}
    
    • 检查并处理参数,之前提到的如果不是函数function,就忽略,这个忽略是指原样返回value或者reason。
     // 定义一个工具函数:用来判断参数是不是函数function
     isFunction(param) {
         return typeof param === 'function';
     }
    
     then(onFulfilled, onRejected) {
     //判断是不是函数,并用新的变量进行接收
         const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
             return value
         }
         const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
             throw reason;
         };
     }
    
    • .then 方法的返回值整体是一个Promise,所以咱们先用Promise来包裹一下。
     then(onFulfilled, onRejected) {
         const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
             return value
         }
         const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
             throw reason;
         };
         const promise2 = new MPromise((resolve, reject) => {})
         return promise2
     }
    
    • 根据当前Promise的状态调用不同的函数
     then(onFulfilled, onRejected) {
         const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
             return value
         }
         const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
             throw reason;
         };
         //new Promise().then(.....)
         const promise2 = new MPromise((resolve, reject) => {
             switch (this.status) {
                 case FULFILLED: {
                     realOnFulfilled()
                     break;
                 }
                 case REJECTED: {
                     realOnRejected()
                     break;
                 }
             }
         })
         return promise2
    
     }
    
    • 有人可能会有疑问,这样写, 是在then函数被调用的瞬间就会执行. 那这时候如果status还没变成fulfilled或者rejected怎么办, 很有可能还是pending的. 所以我们需要一个状态的监听机制, 当状态变成fulfilled或者rejected后, 再去执行callback。

      (1). 那么我们首先要拿到所有的callback, 然后才能在某个时机去执行他. 新建两个数组, 来分别存储成功和失败的回调, 调用then的时候, 如果还是pending就存入数组.

      // 存储执行resolve 改变状态的数组
      FULFILLED_CALLBACK_LIST = [];
      // 存储执行reject 改变状态的数组
      REJECTED_CALLBACK_LIST = [];
      constructor(fn){
        ...
      }
      
      then(onFulfilled, onRejected) {
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
          return value
        }
        const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
          throw reason;
        };
        const promise2 = new MPromise((resolve, reject) => {
          switch (this.status) {
              case FULFILLED: {
                  realOnFulfilled()
                  break;
              }
              case REJECTED: {
                  realOnRejected()
                  break;
              }
              case PENDING: {
                  //pending 时候将回调函数存储
                  this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled)
                  this.REJECTED_CALLBACK_LIST.push(realOnRejected)
              }
          }
        })
        return promise2
      }
      

      (2). 在status发生变化的时候, 就执行所有的回调. 这里咱们用一下es6的getter和setter. 这样更符合语义, 当status改变时, 去做什么事情. (当然也可以顺序执行, 在给status赋值后, 下面再加一行forEach)

        _status = PENDING;
      
        get status() { //监听status状态
            return this._status;
        }
      
        set status(newStatus) {
            //重新赋值status
            this._status = newStatus;
            //根据监听到的状态进行处理
            switch (newStatus) {
                case FULFILLED: {
                    this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                        callback(this.value);//值得注意的是这里传入的参数是this.value
                    });
                    break;
                }
                case REJECTED: {
                    this.REJECTED_CALLBACK_LIST.forEach(callback => {
                        callback(this.reason);
                    });
                    break;
                }
            }
        }
      
    • then的返回值: 上面只是简单说了下, then的返回值是一个Promise, 那么接下来具体讲一下返回promise的value和reason是什么.

      (1). 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。(这样的话, 我们就需要手动catch代码,遇到报错就reject)

      then(onFulfilled, onRejected) {
       const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
           return value
       }
       const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
           throw reason;
       };
       const promise2 = new MPromise((resolve, reject) => {
          //生命新的变量进行处理
           const fulfilledMicrotask = () => {
              //遇到异常就进行reject
               try {
                   realOnFulfilled(this.value);
               } catch (e) {
                   reject(e)
               }
           };
           const rejectedMicrotask = () => {
               try {
                   realOnRejected(this.reason);
               } catch (e) {
                   reject(e);
               }
           }
      
           switch (this.status) {
               case FULFILLED: {
                   fulfilledMicrotask()
                   break;
               }
               case REJECTED: {
                   rejectedMicrotask()
                   break;
               }
               case PENDING: {
                  //替换成新的任务变量
                  this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                   this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
               }
           }
       })
       return promise2
      }
      

      (2). 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值

      (3). 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。 这里咱们其实已经在参数检查的时候做过了, 也就是这段代码

      const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
        return value
      }
      const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
        throw reason;
      };
      

      (4)如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行resolvePromise方法

      then(onFulfilled, onRejected) {
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
            return value
        }
        const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
            throw reason;
        };
        const promise2 = new MPromise((resolve, reject) => {
            const fulfilledMicrotask = () => {
                try { 
                  //onFulfilled执行之后的返回值  => x
                    const x = realOnFulfilled(this.value);
                    this.resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e)
                }
            };
            const rejectedMicrotask = () => {
                try {
                    const x = realOnRejected(this.reason);
                    this.resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }
      
            switch (this.status) {
                case FULFILLED: {
                    fulfilledMicrotask()
                    break;
                }
                case REJECTED: {
                    rejectedMicrotask()
                    break;
                }
                case PENDING: {
                    this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                    this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
                }
            }
        })
        return promise2
      }
      
    • resolvePromise

      resolvePromise(promise2, x, resolve, reject) {
       // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
       // 这是为了防止死循环
       if (promise2 === x) {
         return reject(new TypeError('The promise and the      return value are the same'));
       }
       if (x instanceof MPromise) {
        // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
        // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
        queueMicrotask(() => {
            x.then((y) => {
                this.resolvePromise(promise2, y, resolve, reject);
            }, reject);
        })
       } else if (typeof x === 'object' || this.isFunction(x)) {
        // 如果 x 为对象或者函数
        if (x === null) {
            // null也会被判断为对象
            return resolve(x);
        }
      
        let then = null;
      
        try {
            // 把 x.then 赋值给 then 
            then = x.then;
        } catch (error) {
            // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
            return reject(error);
        }
      
        // 如果 then 是函数
        if (this.isFunction(then)) {
            let called = false;
            // 将 x 作为函数的作用域 this 调用
            // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做ejectPromise
            try {
                then.call(
                    x,
                    // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
                    (y) => {
                        // 需要有一个变量called来保证只调用一次.
                        if (called) return;
                        called = true;
                        this.resolvePromise(promise2, y, resolve, reject);
                    },
                    // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                    (r) => {
                        if (called) return;
                        called = true;
                        reject(r);
                    });
            } catch (error) {
                // 如果调用 then 方法抛出了异常 e:
                if (called) return;
                // 否则以 e 为据因拒绝 promise
                reject(error);
            }
          } else {
            // 如果 then 不是函数,以 x 为参数执行 promise
            resolve(x);
          }
        } else {
        // 如果 x 不为对象或者函数,以 x 为参数执行 promise
        resolve(x);
       }
      }
      
    • onFulfilled 和 onRejected 是微任务, 咱们可以用queueMicrotask包裹执行函数

    const fulfilledMicrotask = () => {
    //创建微任务
     queueMicrotask(() => {
         try {
             const x = realOnFulfilled(this.value);
             this.resolvePromise(promise2, x, resolve, reject);
         } catch (e) {
             reject(e)
         }
     })
    };
    const rejectedMicrotask = () => {
     queueMicrotask(() => {
         try {
             const x = realOnRejected(this.reason);
             this.resolvePromise(promise2, x, resolve, reject);
         } catch (e) {
             reject(e);
         }
     })
    }
    
    • catch 方法
    catch (onRejected) {
       return this.then(null, onRejected);
    }
    
    • resolve 方法:将现有对象转为Promise对象,如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。Tips:注意这是一个静态方法, 因为咱们是通过Promise.resolve调用的, 而不是通过实例去调用的.
    static resolve(value) {
     if (value instanceof MPromise) {
         return value;
     }
    
     return new MPromise((resolve) => {
         resolve(value);
     });
    }
    
    • reject方法:返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数。
    static reject(reason) {
     return new MPromise((resolve, reject) => {
         reject(reason);
     });
    }
    
    • race方法 :const p = Promise.race([p1, p2, p3]);该方法是将多个 Promise 实例,包装成一个新的 Promise 实例。只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
static race(promiseList) {
    return new MPromise((resolve, reject) => {
        const length = promiseList.length;

        if (length === 0) {
            return resolve();
        } else {
            for (let i = 0; i < length; i++) {
                MPromise.resolve(promiseList[i]).then(
                    (value) => {
                        return resolve(value);
                    },
                    (reason) => {
                        return reject(reason);
                    });
            }
        }
    });

}
  • all方法 :接收一个promise数组,全部成功才返回,只要有个一抛出异常就reject
static
all(promiseList){
    return new MPromise((resolve, reject)=>{
        if(!Array.isArray(promiseList)){
            reject('input param must be a list')
        }
        let size = promiseList.length
        if(size === 0){
            resolve()
        }else{
            let count = 0
            let res = []
            for(let i=0; i<size; i++){
                MPromise.resolve(promiseList(i)).then((value)=>{
                    res[i] = value
                    if(count++ === size){
                        resolve(res)
                    }
                }).catch((reason)=>{
                    reject(reason)
                })
            }
        }
    })
}

全代码

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MPromise {

    FULFILLED_CALLBACK_LIST = [];
    REJECTED_CALLBACK_LIST = [];
    _status = PENDING;
    constructor(fn) {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
        try {
            fn(this.resolve.bind(this), this.reject.bind(this));
        } catch (e) {
            this.reject(e);
        }
    }

    get status() {
        return this._status;
    }

    set status(newStatus) {
        this._status = newStatus;
        switch (newStatus) {
            case FULFILLED: {
                this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                    callback(this.value);
                });
                break;
            }
            case REJECTED: {
                this.REJECTED_CALLBACK_LIST.forEach(callback => {
                    callback(this.reason);
                });
                break;
            }
        }
    }

    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }

    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }

    then(onFulfilled, onRejected) {
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
            return value
        }
        const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
            throw reason;
        };
        const promise2 = new MPromise((resolve, reject) => {
            const fulfilledMicrotask = () => {
                queueMicrotask(() => {
                    try {
                        const x = realOnFulfilled(this.value);
                        this.resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                })
            };
            const rejectedMicrotask = () => {
                queueMicrotask(() => {
                    try {
                        const x = realOnRejected(this.reason);
                        this.resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            }

            switch (this.status) {
                case FULFILLED: {
                    fulfilledMicrotask()
                    break;
                }
                case REJECTED: {
                    rejectedMicrotask()
                    break;
                }
                case PENDING: {
                    this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                    this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
                }
            }
        })
        return promise2

    }

    catch(onRejected) {
        return this.then(null, onRejected);
    }

    isFunction(param) {
        return typeof param === 'function';
    }

    resolvePromise(promise2, x, resolve, reject) {
        // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
        // 这是为了防止死循环
        if (promise2 === x) {
            return reject(new TypeError('The promise and the return value are the same'));
        }

        if (x instanceof MPromise) {
            // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
            // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
            queueMicrotask(() => {
                x.then((y) => {
                    this.resolvePromise(promise2, y, resolve, reject);
                }, reject);
            })
        } else if (typeof x === 'object' || this.isFunction(x)) {
            // 如果 x 为对象或者函数
            if (x === null) {
                // null也会被判断为对象
                return resolve(x);
            }

            let then = null;

            try {
                // 把 x.then 赋值给 then 
                then = x.then;
            } catch (error) {
                // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
                return reject(error);
            }

            // 如果 then 是函数
            if (this.isFunction(then)) {
                let called = false;
                // 将 x 作为函数的作用域 this 调用
                // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
                try {
                    then.call(
                        x,
                        // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
                        (y) => {
                            // 需要有一个变量called来保证只调用一次.
                            if (called) return;
                            called = true;
                            this.resolvePromise(promise2, y, resolve, reject);
                        },
                        // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                        (r) => {
                            if (called) return;
                            called = true;
                            reject(r);
                        });
                } catch (error) {
                    // 如果调用 then 方法抛出了异常 e:
                    if (called) return;

                    // 否则以 e 为据因拒绝 promise
                    reject(error);
                }
            } else {
                // 如果 then 不是函数,以 x 为参数执行 promise
                resolve(x);
            }
        } else {
            // 如果 x 不为对象或者函数,以 x 为参数执行 promise
            resolve(x);
        }
    }

    static resolve(value) {
        if (value instanceof MPromise) {
            return value;
        }

        return new MPromise((resolve) => {
            resolve(value);
        });
    }

    static reject(reason) {
        return new MPromise((resolve, reject) => {
            reject(reason);
        });
    }

    static race(promiseList) {
        return new MPromise((resolve, reject) => {
            const length = promiseList.length;

            if (length === 0) {
                return resolve();
            } else {
                for (let i = 0; i < length; i++) {
                    MPromise.resolve(promiseList[i]).then(
                        (value) => {
                            return resolve(value);
                        },
                        (reason) => {
                            return reject(reason);
                        });
                }
            }
        });

    }
    static all(promiseList) {
        return new MPromise((resolve, reject) => {
            if (!Array.isArray(promiseList)) {
                reject('input param must be a list')
            }
            let size = promiseList.length
            if (size === 0) {
                resolve()
            } else {
                let count = 0
                let res = []
                for (let i = 0; i < size; i++) {
                    MPromise.resolve(promiseList(i)).then((value) => {
                        res[i] = value
                        if (count++ === size) {
                            resolve(res)
                        }
                    }).catch((reason) => {
                        reject(reason)
                    })
                }
            }
        })
    }
}