手写Promise(符合PromiseA+规范)

72 阅读2分钟
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class MyPromise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (value instanceof MyPromise) {
        return value.then(resolve, reject);
      }

      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    };

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

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

  then(onFulfilled, onRejectd) {
    onFulfilled = isFunction(onFulfilled) ? onFulfilled : v => v;
    onRejectd = isFunction(onRejectd) ? onRejectd : e => { throw e };

    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(x, promise2, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejectd(this.reason);
            resolvePromise(x, promise2, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      } else if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(x, promise2, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejectd(this.reason);
              resolvePromise(x, promise2, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });
      }
    });

    return promise2;
  }

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

  finally(callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason });
    });
  }

}

MyPromise.deferred = function () {
  let dfd = {};
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });

  return dfd;
};

MyPromise.resolve = function (value) {
  if (value instanceof MyPromise) {
    return value;
  }

  if (isThenable(value)) {
    return new MyPromise((resolve, reject) => {
      value.then(resolve, reject);
    });
  }

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

MyPromise.reject = function (reason) {
  return new MyPromise((resolve, reject) => {
    reject(reason);
  });
};

MyPromise.all = function (promises) {
  return new MyPromise((resolve, reject) => {
    // 参数是否可迭代
    if (!isIterable(promises)) {
      return reject(new TypeError('Arguments is not iterable'));
    }

    let index = -1;
    let count = 0;
    const result = [];
    const length = typeof promises.length === 'number' ? promises.length : promises.size;
    // 参数为空,直接fulfilled
    if (length === 0) {
      return resolve(result);
    }

    function processSuccess(i, value) {
      result[i] = value;

      if (++count === length) {
        resolve(result);
      }
    }

    for (let p of promises) {
      let i = ++index;
      if (isThenable(p)) {
        p.then(value => {
          processSuccess(i, value);
        }, reject);
      } else {
        processSuccess(i, p);
      }
    }
  });
};

MyPromise.allSettled = function (promises) {
  return new MyPromise((resolve, reject) => {
    // 参数是否可迭代
    if (!isIterable(promises)) {
      return reject(new TypeError('Arguments is not iterable'));
    }

    let index = -1;
    let count = 0;
    const result = [];
    const length = typeof promises.length === 'number' ? promises.length : promises.size;
    // 参数为空,直接fulfilled
    if (length === 0) {
      return resolve(result);
    }

    function processSuccess(i, valueOrReason, status) {
      result[i] = {
        status,
        [status === 'fulfilled' ? 'value' : 'reason']: valueOrReason,
      };

      if (++count === length) {
        resolve(result);
      }
    }

    for (let p of promises) {
      let i = ++index;
      if (isThenable(p)) {
        p.then(value => {
          processSuccess(i, value, 'fulfilled');
        }, reason => {
          processSuccess(i, reason, 'rejected');
        });
      } else {
        processSuccess(i, p, 'fulfilled');
      }
    }
  });
};

// The returned promise remains pending forever if the iterable passed is empty.
MyPromise.race = function (promises) {
  return new MyPromise((resolve, reject) => {
    // 参数是否可迭代
    if (!isIterable(promises)) {
      return reject(new TypeError('Arguments is not iterable'));
    }
    // 参数为空,永远pending
    for (let p of promises) {
      MyPromise.resolve(p).then(resolve, reject);
    }
  });
};

MyPromise.any = function (promises) {
  return new MyPromise((resolve, reject) => {
    // 参数是否可迭代
    if (!isIterable(promises)) {
      return reject(new TypeError('Arguments is not iterable'));
    }
    // 参数为空,reject
    let count = 0;
    const errors = [];
    const length = typeof promises.length === 'number' ? promises.length : promises.size;
    if (length === 0) {
      return reject(new AggregateError(errors, 'All promises were rejected'));
    }

    function countError(err) {
      errors.push(err);

      if (++count === length) {
        reject(new AggregateError(errors, 'All promises were rejected'));
      }
    }

    for (let p of promises) {
      MyPromise.resolve(p).then(resolve, countError);
    }
  });
};



function isObject(value) {
  return typeof value === 'object' && value !== null;
}

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

function isThenable(value) {
  if (isObject(value) || isFunction(value)) {
    try {
      let then = value.then;
      return isFunction(then);
    } catch (e) {
      return false;
    }
  }

  return false;
}

function isIterable(value) {
  return isFunction(value[Symbol.iterator]);
}

function resolvePromise(x, promise2, resolve, reject) {
  if (x === promise2) {
    return reject(new TypeError('x === promise2'));
  }

  if (isObject(x) || isFunction(x)) {
    let called = false;
    try {
      let then = x.then;
      if (isFunction(then)) {
        then.call(x, y => {
          if (called) return;
          called = true;
          resolvePromise(y, promise2, resolve, reject);
        }, e => {
          if (called) return;
          called = true;
          reject(e);
        });
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}

module.exports = MyPromise;