手写符合Promises/ A+规范的Promise

212 阅读2分钟

Promises/A+规范是一个开放、健全且通用的 JavaScript Promise 标准

//promise内部有三种状态
const PENDING = 'pending';
const FULLFILLED = 'fullfilled';
const REJECTED = 'rejected';

class Promise {

//解析x,x可能的值:promise, thanble对象和普通值;promise对象和thenable对象需要特殊处理
    static resolvePromise(promise2, x, resolve, reject) {
      if(promise2 === x) {
        //每次返回的是个新的promise,不能返回原来的promise,否则报错
        return reject(new TypeError('Chaining cycle detected for promise'));
      }

      let called = false; //防止被多次调用

      if(x instanceof Promise) {
        //x是个promise

        x.then(value => {
          //这里返回的仍然可能是promise
          if(called) return;
          called = true;
          Promise.resolvePromise(promise2, value, resolve, reject);
        }, reason => {
          if(called) return;
          called = true;
          reject(reason)
        })
      }else if(x !== null && typeof x === 'object' || typeof x == 'function') {

        try {
          const then = x.then;
          if(typeof then == 'function') {
          //如果x是thenable对象
            const res = then.call(x, (value) => {
              if(called) return;
              called = true;
              Promise.resolvePromise(promise2, value, resolve, reject);  
            }, (reason) => {
              if(called) return;
              called = true;
              reject(reason)
            });

          }else {
          //不是thenable对象,是个普通的对象。直接resolve
            if(called) return;
            called = true;
            resolve(x)
          }
        }catch(e) {
          if(called) return;
          called = true;
          reject(e)
        }

      }else {
        //x为一个普通值
        resolve(x)
      }
    }


  constructor(executor) {

    //检测executor类型
    if(typeof executor !== 'function') {
      throw new TypeError(`Promise resolver ${executor} is not a function`)
    }
    this.executor = executor;
    this.status = PENDING;
    this.result = undefined;
    this.reason = undefined;
    this.onFullfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    this.resolve = (result) => {
      if(this.status !== PENDING) return;
      this.status = FULLFILLED;
      this.result = result;
      this.onFullfilledCallbacks.forEach(fn => fn(this.result))

    };

    this.reject = (reason) => {
      if(this.status !== PENDING) return;
      this.status = REJECTED;
      this.reason = reason;
      this.onRejectedCallbacks.forEach(fn => fn(this.reason))
    };

    try {
      executor(this.resolve, this.reject);
    }catch(e) {
      this.reject(e);
    }
}

  then(successHandler, rejectHandler) {
    if(typeof successHandler !== 'function') {
      successHandler = s => s
    }
    
    //注意:这里是直接抛出错误,因为rejectHandler如果不是函数,就相当于没有处理这个错误,错误继续传递
    if(typeof rejectHandler !== 'function') {
      rejectHandler = reason => {throw reason}
    }

    let promise2 = new Promise((resolve, reject) => {
      //实现链式调用
      if(this.status === FULLFILLED) {
        queueMicrotask(() => {
          try {
            const x = successHandler(this.result);
            // resolve(x)
            Promise.resolvePromise(promise2, x, resolve, reject)
          }catch(e) {
            reject(e)
          }
        })
      }

      if(this.status === REJECTED) {
        queueMicrotask(() => {
          try {
            const x = rejectHandler(this.reason);
            Promise.resolvePromise(promise2, x, resolve, reject)
          }catch(e) {
            reject(e)
          }
        })
      }

      if(this.status === PENDING) {
      //这里如果此时状态还是PENDING,需要先把处理函数存储起来,储存的时候注意要放在微任务中执行
        this.onFullfilledCallbacks.push((result) => {
          queueMicrotask(() => {
            try {
              const x = successHandler(result);
              Promise.resolvePromise(promise2, x, resolve, reject)
            }catch(e) {
              reject(e)
            }
          })
        });
        this.onRejectedCallbacks.push((reason) => {
          queueMicrotask(() => {
            try {
              const x = rejectHandler(reason);
              Promise.resolvePromise(promise2, x, resolve, reject)
            }catch(e) {
              reject(e)
            }
          })
        });
      }
    })

    return promise2;
  }
}