前端进阶之PromiseA+规范

PromiseA+规范文档

术语 

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

规范

Promise States

promise应该有三种状态. 要注意他们之间的流转关系.

  1. pending
    1. 初始的状态,可改变
    2. 一个promise在resolve或者reject前都处于这个状态。
    3. 可以通过 resolve -> fulfilled 状态;

    4. 可以通过 reject -> rejected 状态;

  2. fulfilled
    1. 最终态, 不可变.

    2. 一个promise被resolve后会变成这个状态.

    3. 必须拥有一个value值

  3. rejected
    1. 最终态, 不可变.

    2. 一个promise被reject后会变成这个状态

    3. 必须拥有一个reason

Tips: 总结一下, 就是promise的状态流转是这样的

pending -> resolve(value) -> fulfilled

pending -> reject(reason) -> rejected

then

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

  1. 参数要求

    1. onFulfilled 必须是函数类型, 如果不是函数, 应该被忽略.

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

  2. onFulfilled 特性

    1. 在promise变成 fulfilled 时,应该调用 onFulfilled, 参数是value

    2. 在promise变成 fulfilled 之前, 不应该被调用.

    3. 只能被调用一次(所以在实现的时候需要一个变量来限制执行次数)

  3. onRejected 特性

    1. 在promise变成 rejected 时,应该调用 onRejected, 参数是reason

    2. 在promise变成 rejected 之前, 不应该被调用.

    3. 只能被调用一次(所以在实现的时候需要一个变量来限制执行次数)

  4. onFulfilled 和 onRejected 应该是微任务(A+规范里面并没有规定,但ES6使用的是微任务)

    1. 这里用queueMicrotask来实现微任务的调用.

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

    1. promise状态变成 fulfilled 后,所有的 onFulfilled 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onFulfilled的回调)

    2. promise状态变成 rejected 后,所有的 onRejected 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onRejected的回调)

  6. 返回值

    1. then 应该返回一个promise

    2. onFulfilled 或 onRejected 执行的结果为x, 调用 resolvePromise

    3. 如果 onFulfilled 或者 onRejected 执行时抛出异常e, promise2需要被reject

    4. 如果 onFulfilled 不是一个函数, promise2 以promise1的value 触发fulfilled

    5. 如果 onRejected 不是一个函数, promise2 以promise1的reason 触发rejected

  7. resolvePromise

    1. 如果 promise2 和 x 相等,那么 reject TypeError
    2. 如果 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.

    3. 如果 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 已经被调用,那么忽略

      则,reject promise with e as the reason

      如果 then 不是一个function. fulfill promise with x.

实现:

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

class MPromise {
  FULFILLED_CALLBACK_LIST = [];
  REJECTED_CALLBACK_LIST = [];
  _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)
    }
  }

  get status() {
    console.log('get status')
    return this._status
  }

  set status(newstatus){
    console.log('newstatus', 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.value)
        })
        break;
      }
    }
  }


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

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

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

    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);
            }
        })
      }
      console.log(this.status)
      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(promise2, x, resolve, reject){
    if(promise2===x){
      return reject(new TypeError('The promise and the return value are the same'));
    }
    if(x instanceof MPromise){
      queueMicrotask(() => {
        x.then((y) => {
            this.resolvePromise(promise2, y, resolve, reject);
        }, reject);
      })
    } else if(typeof x === 'object' || this.isFunction(x)){
      if ( x === null) {
        return resolve(x)
      }
      let then = null;
      try{
        then = x.then
      }catch(err){
        return  reject(err)
      }
      if(this.isFunction(then)){
        let called = false;
        try{
          then.call(
            x,
            (y) => {
                if (called) return;
                called = true;
                this.resolvePromise(promise2, y, resolve, reject);
            },
            (r) => {
                if (called) return;
                called = true;
                reject(r);
            });
        }catch(e){
          if(called){
            return
          }
          reject(e)
        }

      } else {
        resolve(x)
      }

    } else {
      return resolve(x)
    }
  }

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

  isFunction(params){
    return typeof params === 'function'
  }

  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(promiseArray) {
    return new MPromise(function(resolve, reject){
      if (!Array.isArray(promiseArray)){
        return reject(new TypeError('arguments must be array'))
      }
      const promiseNum = promiseArray.length
      const res = []
      let counter = 0
      for(let i = 0; i <promiseNum; i++){
        MPromise.resolve(promiseArray[i]).then(value=>{
          counter ++
          res[i] = value
          if(counter === promiseNum){
            resolve(res)
          }
        }).catch(e=>{
          reject(e)
        })
      }
    })
  }
}

Promise链式调用执行顺序练习题

第一题

Promise.resolve()
    .then(() => {
        console.log(0);
        setTimeout(() => {
            console.log('宏任务');
        }, 0);
        return 4
    })
    .then((res) => {
        console.log(res);
    });

Promise.resolve().then(() => {
        console.log(1);
    })
    .then(() => {
        console.log(2);
    })
    .then(() => {
        console.log(3);
    })
    .then(() => {
        console.log(5);
    })
    .then(() => {
        console.log(6);
    })
    .then(() => {
        console.log(7);
    })

打印结果:0、1、4、2、3、5、6、7、宏任务

原因(本人自己的理解):0跟1同一时间进入执行栈,先依次执行0跟1,执行完成之后,开始继续找任务,因为return 4 本身就是同步任务(给下一题做埋点),所以console.log(res)console.log(2)同一时间进入执行栈,依次打印4跟2,第一个promise链式调用结束,依次把第二个promise链式调用的结果输出完成,最后执行宏任务。

第二题

Promise.resolve()
    .then(() => {
        console.log(0);
        return Promise.resolve(4);
    })
    .then((res) => {
        console.log(res);
    });

Promise.resolve().then(() => {
        console.log(1);
    })
    .then(() => {
        console.log(2);
    })
    .then(() => {
        console.log(3);
    })
    .then(() => {
        console.log(5);
    })
    .then(() => {
        console.log(6);
    })
    .then(() => {
        console.log(7);
    })

打印结果: 0,1,2,3,4,5,6,7,宏任务

原因:首先跟上面一样,0跟1同时进入执行栈,然后执行完成之后,重新找任务,首先找到return Promise.resolve(4)console.log(2), 在我们封装的promise中,如果接收到一个promise,那么会加一层微任务,所以实际上queueMicrotaskconsole.log(2)同时进入队列一次执行,所以打印2。

 紧接着执行栈空了重新找任务,开始执行Promise.resolve(4)console.log(3),执行完成之后Promise.resolve(4)返回了4,console.log(3)打印结果,所以打印了3,然后这个时候才是开始真正执行return 4,这个时候就回到了第一题,console.log(res)console.log(5)同时进入执行栈,依次打印4跟5。所以最终结果为0,1,2,3,4,5,6,7。

第三题

Promise.resolve()
    .then(async () => {
        console.log(0);
        setTimeout(() => {
            console.log('宏任务');
        }, 0);
        return Promise.resolve(4);
    })
    .then((res) => {
        console.log(res);
    });

Promise.resolve().then(() => {
        console.log(1);
    })
    .then(() => {
        console.log(2);
    })
    .then(() => {
        console.log(3);
    })
    .then(() => {
        console.log(5);
    })
    .then(() => {
        console.log(6);
    })
    .then(() => {
        console.log(7);
    })

打印结果:0,1,2,3,5,4,6,7,宏任务

原因:同第二题的解释,为什5在4的后面,原因是因为第一个promise链式调用的时候,因为加了一个async,导致在微任务队列里面把console.log(res)这个任务又往后推了一步,所以由原先的3、4、5变成3、5、4。

封装Promise跟以上练习题原因非标准答案,博客主自己的理解~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值