Promise源码模拟

151 阅读3分钟
/**
 * 1. Promise是一个类,创建类的时候需要传入一个执行器,该执行器立即被执行
 * 2. 三种状态:pending等待 fulfilled成功 rejected失败
 * 3. 执行器中的两个参数 resolve,reject 是用来改变状态的
 * resolve 改变状态为 pending -> fulfilled
 * reject 改变状态为 pending -> rejected
 * 状态一旦确定不能再改变
 * 4. then方法有两个函数类型的参数,第一个参数为成功回调,第二个参数为失败回调;
 * 内部判断状态,如果成功 则执行成功回调,成功回调函数的参数为 resolve 传入的参数
 * 如果失败则调用 失败回调,失败回调函数的参数为 reject 传入的参数
 * 5. 考虑执行器内部是异步API的情况
 * 6. then方法可以被链式调用,后面的then方法成功回调函数的参数是上一个then回调函数的返回值
 * 7. 同一个Promise对象的then方法可以被多次调用,并依次执行
 */
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败

class MyPromise{
  status = PENDING;
  value = undefined;
  error = undefined;
  successCallbacks = [];
  failCallbacks = [];
  constructor(executor){
    try{
      executor(this.resolve, this.reject)
    }catch(e){
      this.reject(e);
    }
  }
  // 箭头函数 绑定this作用域
  resolve = (value)=>{
    if (this.status === PENDING){
      this.status = FULFILLED;
      this.value = value;
      // 当resolve被异步执行时,调用已经存储的回调函数
      while(this.successCallbacks.length){
        this.successCallbacks.shift()();
      }
    }
  }
  // 箭头函数 绑定this作用域
  reject = (err)=>{
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.error = err;
      // 当reject被异步执行时,调用已经存储的回调函数
      while (this.failCallbacks.length) {
        this.failCallbacks.shift()();
      }
    }
  }

  then(successCallback,failCallback){
    // then方法参数可选
    failCallback = failCallback || (error=> {throw error});
    // successCallback非函数时,则透传
    if (!(successCallback instanceof Function)){
      successCallback = ()=>this.value
    }

    let promise2 = new MyPromise((resolve,reject)=>{
      if (this.status === FULFILLED) {
        /***
         * 如果 x 是一个普通值,则直接 resolve 返回
         * 如果 x 是一个Promise对象,则需要判断Promise返回的状态,即在 then 中传入resolve、reject
         * 如果 x 就是 promise2 自身,为避免无限回调,需要报错
         */
        // 用 setTimeout 使代码在同步代码后执行,避免 promise2 未声明先使用报错
        setTimeout(() => {
          try {
            let x = successCallback(this.value)
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error)
          }
        });
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.error)
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error)
          }
        });
      } else {
        // pending状态,缓存回调函数
        this.successCallbacks.push(()=>{
          setTimeout(() => {
            try {
              let x = successCallback(this.value)
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error)
            }
          });
        });
        this.failCallbacks.push(()=>{
          setTimeout(() => {
            try {
              let x = failCallback(this.error)
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error)
            }
          });
        })
       }
    })
    return promise2;
  }
  /**
   * 无论失败还是成功,都会被执行
   * finally 返回的结果将在下一个 then 中体现
   */
  finally(callback){
    return this.then(function(res){
      // 若callback函数返回一个Promise对象,则需要等该Promise执行后再向下运行
      return  MyPromise.resolve(callback()).then(()=>res)
    }, function(err){
      return MyPromise.resolve(callback()).then(() => {throw err;})
    })
  }
  /**
   * 捕获错误
   */
  catch(fun){
    return this.then(undefined, fun)
  }
  /**
   * all 是静态类方法,返回Promise对象
   * then方法的成功回调函数参数是数组
   */
  static all(arr){
    const result = [];
    return new Promise(function(resolve,reject){
      // 由于then方法中有 settimeout 代码块,则需要判断result的长度是否等于arr的长度
      // 长度相等,则说明arr中的结果都成功返回了,可以执行resolve
      function addResult(index,res){
        result[index] = res;
        if (result.length === arr.length){
          resolve(result);
        }
      }

      arr.map((item,idx)=>{
        if(item instanceof MyPromise){
          // 如果是Promise对象,则需要判断Promise对象的返回结果
          item.then(res => addResult(idx, res), reject)
        }else{
          addResult(idx, item);
        }
      })

    })
  }
  /**
   * 如果value是普通值,则包装成Promise返回
   * 如果value是Prmise对象,直接返回
   */
  static resolve(value){
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }
}

function resolvePromise(promise2,x,resolve,reject){
  if(promise2 === x){
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if(x instanceof MyPromise){
    return x.then(resolve,reject)
  }else{
    return resolve(x);
  }
}