带你手写Promise A+规范Promise

417 阅读8分钟

近期学习了promise A+规范,想做个记录并分享给有需要的兄弟姐妹,废话不多说,能搜索到这个的肯定已经知道promise是什么东西了,我们直接开始。

1617634974(1).jpg

Promise A+ 规范

英文好的小伙伴可以直接看原文档 首先我们先来了解Promise的相关术语,以便后面我们提到大家知道其对应的概念是什么。

术语

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

规范

下面我们就开始看看PromiseA+规范。

promise 状态

promise必须处于以下三种状态中的一种Pending,Fulfilled,Rejected。

  1. Pending
  • 初始的状态,可以迁移至执行态或拒绝态。
    
  • 一个proimse在resolve或者reject前都处于这个状态。
    
  1. Fulfilled
  • 最终态,不能迁移至其他任何状态。
    
  • 必须拥有一个不可变的终值value。
    
  • promise被resolve后会变成这个状态。
    
  1. Rejected
  • 最终态,不能迁移至其他任何状态。
    
  • 必须拥有一个不可变的拒因reason。
    
  • promise被reject后会变成这个状态。
    

then

一个 promise 必须提供一个 then 方法以访问其当前值(value)、终值和据因(reason)。

//promise 的 then 方法接受两个参数
promise.then(onFulfilled,onRejected)
  1. 参数要求
  • then的两个参数都为可选参数
  • 如果 onFulfilled 不是函数,其必须被忽略
  • 如果 onRejected 不是函数,其必须被忽略
  1. onFulfilled 特性
  • 在promise变成fulfilled时,应该调用onFulfilled,参数为value。
  • 在promise变成fulfilled之前,不应该被调用。
  • 只能被调用一次。
  1. onRejected 特性
  • 在promise变成rejected时,应该调用onRejected,参数为reason。
  • 在promise变成rejected之前,不应该被调用。
  • 只能被调用一次。
  1. 调用时机
  • onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用(onFulfilled 和 onRejected应该为微任务)
  1. then方法可以被调用多次
  • 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调。
  • 当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调。
  1. 返回值
  • then应该返回一个promise
promise2 = promise1.then(onFulfilled,onRejected)
  • onFulfilled 或 onRejected 执行的结果为x,调用则运行下面的 Promise 解决过程:[[Resolve]](promise2, x) 注:这个解决过程我们用一个resolvePromise方法来写。
  • 如果onFulfilled不是一个函数,promise2 以 promise1的value 触发fulfilled
  • 如果onRejected 不是一个函数,promise2 以 promise1的reason触发rejected 注:理解上面的“返回”部分非常重要,即:不论 promise1 被 reject 还是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected。
  1. resolvePromise
   resolvePromise(promise2, x, resolve, reject)
  • 如果 promise2 和 x 相等,那么 reject TypeError
  • 如果 x 是一个 promsie 如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected. 如果 x 被 fulfilled, fulfill promise with the same value. 如果 x 被 rejected, reject promise with the same reason.
  • 如果 x 是一个 object 或者 是一个 function
    let then = x.then.
    如果 x.then 这步出错,那么 reject promise with e as the reason. 如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
    resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
    rejectPromise 的 入参是 r, reject promise with r.
    如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
  • 如果调用then抛出异常e
    如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略 否则以 e 为据因拒绝 promise
  • 如果 then 不是一个function. fulfill promise with x.

规范了解完了下面我们开始手写promise!!!!

这里我们用es6的class来写我们的promise

1.定义一个class来实现Promise

class MPromise{

}

好了,我们的promise类写好了。。。。接下来我们来按照规范来实现一个符合promise A+ 的promise吧。

  1. 定义三种状态类型
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MPromise{
    _status = PENDING;
}
  1. 初始化状态,value值,reason值
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MPromise{
    _status = PENDING;
    constructor(){
            this.status = PENDING;
            this.value = null;
            this.reason = null;
    }
}
  1. resolve 和 reject 方法 根据刚才的规范,我们知道两个方法要改变promise的状态,要注意,当promise是pending状态才能更改,当promise更改为最终态后就不可改变了。
    resolve(value){
        //fulfilled与rejected为最终态不可改变,所以只能当status为pending时才可改变status值
        if(this.status === PENDING){
            this.status = FULFILLED
            this.value = value
        }
    };
    reject(reason){
         //fulfilled与rejected为最终态不可改变,所以只能当status为pending时才可改变status值
         if(this.status === PENDING){
            this.status = REJECTED
            this.reason = reason
        }
    };
  1. 在promise中我们还需要一个入参,也就是promise(入参),要注意,入参是一个函数,函数接收resolve和reject两个参数。在初始化promise的时候,就要执行,并且如果报错就要reject抛出
class MPromise {
        _status = PENDING;
        constructor(fn/**入参 */) {
            this.status = PENDING;
            this.value = null;
            this.reason = null;
            // 入参初始化时应该执行执行
            try {
                fn(this.resolve.bind(this), this.reject.bind(this));
            } catch (e) {
                this.reject(e);
            }
        }
        ...(后面的代码目前为resolve方法与reject方法)
    }
  1. then方法(这个关键,有错误请大佬指出)
  • 1.then接收两个参数,onFulfilled和onRejected
then(onFulfilled,onRejected){}
  • 2.如果onFulfilled或onRejected不是function,就忽略(指原样返回value或reason不做处理)
isFunction(param){
    return typeof param === 'function';
}
then(onFulfilled, onRejected){
    //写两个三元表达式给他判断一下是不是function
    const fulFilledFn = this.isFunction(onFulfilled)?onFulfilled:(value)=>{
        return value
    }
    const rejectedFn = this.isFunction(onRejected)?onRejected:(reason)=>{
       //reject直接抛出 reason
       throw reason
    };
}
  • 3.根据当前promise的状态, 调用不同的函数。注意then函数被调用的瞬间就会执行。 那这时候如果status还没变成fulfilled或者rejected。那么我们首先要拿到所有的回调, 然后才能在某个时机去执行他。
    
   class MPromise {
       FULFILLED_CALLBACK_LIST = []
       REJECTED_CALLBACK_LIST = []
       _status = PENDING;
           ...(省略前面的代码)
        then(onFulfilled, onRejected) {
           const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
               return value;
           }
           const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
               throw reason;
           };

           switch (this.status) {
               case FULFILLED: {
                   fulFilledFn(this.value);
                   break;
               }
               case REJECTED: {
                   rejectedFn(this.reason);
                   break;
               }
               case PENDING: {
                   this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled);
                   this.REJECTED_CALLBACK_LIST.push(realOnRejected);
                   break;
               }
           }
       }   
       }
  • 4.因为onFulfilled特性onRejected特性,在status变成Fulfilled或Rejected时,需要执行所有的回调,这里我们用es6的getter和setter。
get status() {
            return this._status;
        }

        set 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;
                }
            }
        }
  • 5.then的返回值 5.1 因为then的返回值是个promis。所有我们需要返回一个prmoise。 5.2 如果onFulfilled 或者 onRejected 抛出一个异常e,则promise2必须拒绝执行,并返回拒因e。我们这时就得手动去catch代码,遇到错误的就直接reject抛出

      then(onFulfilled, onRejected) {
          const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
              return value;
          }
          const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
              throw reason;
          };
          //这里我们包装两个tryCatch函数分别为
          //fulFilledFn(onFulfilled的包装函数)的tryCatch函数
          //rejectedFn(onRejected的包装函数)的tryCatch函数
          const fulFilledFnWithCatch = (resolve, reject) => {
              try {
                  fulFilledFn(this.value);
              } catch (e) {
                  reject(e)
              }
          };
    
          const rejectedFnWithCatch = (resolve, reject) => {
              try {
                  rejectedFn(this.reason);
              } catch (e) {
                  reject(e);
              }
          }
    
          switch (this.status) {
              case FULFILLED: {
              //返回一个promise
                  return new MPromise(fulFilledFnWithCatch);
              }
              case REJECTED: {
              //返回一个promise
                  return new MPromise(rejectedFnWithCatch);
              }
              case PENDING: {
              //返回一个promise
                  return new MPromise((resolve, reject) => {
                      this.FULFILLED_CALLBACK_LIST.push(() => fulFilledFnWithCatch(resolve, reject));
                      this.REJECTED_CALLBACK_LIST.push(() => rejectedFnWithCatch(resolve, reject));
                  });
              }
          }
      }
     
    

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

     const fulFilledFnWithCatch = (resolve, reject) => {
             try {
                 fulFilledFn(this.value);
                 resolve(this.value);
             } catch (e) {
                 reject(e)
             }
         };
    

    5.3 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。 需要注意的是,如果promise1的onRejected执行成功了,promise2应该被resolve。(这里很绕,可以运行一个promise看看。第一个then成功执行后promise会执行onFulfilled方法)

    const rejectedFnWithCatch = (resolve, reject) => {
        try {
            rejectedFn(this.reason);
            if (this.isFunction(onRejected)) {
                resolve();
            }
        } catch (e) {
            reject(e);
        }
    }

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

resolvePromise(newPromise, x, resolve, reject)

因为resolvePromise需要一个newPromise参数,所以我们需要把then函数再改造一下。

   then(onFulfilled, onRejected) {
       const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
           return value;
       }
       const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
           throw reason;
       };

       const fulFilledFnWithCatch = (resolve, reject, newPromise) => {
           try {
               if (!this.isFunction(onFulfilled)) {
                   resolve(this.value);
               } else {
                   const x = fulFilledFn(this.value);
                   this.resolvePromise(newPromise, x, resolve, reject);
               }
           } catch (e) {
               reject(e)
           }
       };

       const rejectedFnWithCatch = (resolve, reject, newPromise) => {
           try {
               if (!this.isFunction(onRejected)) {
                   reject(this.reason);
               } else {
                   const x = rejectedFn(this.reason);
                   this.resolvePromise(newPromise, x, resolve, reject);
               }
           } catch (e) {
               reject(e);
           }
       }

       switch (this.status) {
           case FULFILLED: {
               const newPromise = new MPromise((resolve, reject) => fulFilledFnWithCatch(resolve, reject, newPromise));
               return newPromise;
           }
           case REJECTED: {
               const newPromise = new MPromise((resolve, reject) => rejectedFnWithCatch(resolve, reject, newPromise));
               return newPromise;
           }
           case PENDING: {
               const newPromise = new MPromise((resolve, reject) => {
                   this.FULFILLED_CALLBACK_LIST.push(() => fulFilledFnWithCatch(resolve, reject, newPromise));
                   this.REJECTED_CALLBACK_LIST.push(() => rejectedFnWithCatch(resolve, reject, newPromise));
               });
               return newPromise;
           }
       }
   }
  1. resolvePromise
  resolvePromise(newPromise, x, resolve, reject) {
      // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
      // 这是为了防止死循环
      if (newPromise === 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
          // 这个if跟下面判断then然后拿到执行其实重复了,可有可无
          x.then((y) => {
              resolvePromise(newPromise, y, resolve, reject);
          }, reject);
      } else if (typeof x === 'object' || this.isFunction(x)) {
          // 如果 x 为对象或者函数
          // 这个坑是跑测试的时候发现的,如果x是null,应该直接resolve
          if (x === 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) => {
                          // 如果 resolvePromise 和 rejectPromise 均被调用,
                          // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
                          // 实现这条需要前面加一个变量called
                          if (called) return;
                          called = true;
                          resolvePromise(promise, y, resolve, reject);
                      },
                      // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                      (r) => {
                          if (called) return;
                          called = true;
                          reject(r);
                      });
              } catch (error) {
                  // 如果调用 then 方法抛出了异常 e:
                  // 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
                  if (called) return;

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