Skip to content

手写 Promise

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

class MyPromise {
  status = PENDING;
  value = undefined;
  reason = undefined;

  onFulfilledCallbacks = [];
  onRejectedCallbacks = [];

  constructor(executor) {
    const resolve = value => {
      if (value instanceof MyPromise) {
        value.then(resolve, reject);
      } else if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onFulfilledCallbacks.forEach(cb => cb());
      }
    };

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

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

  then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
      onFulfilled = value => avlue;
    }
    if (typeof onRejected !== 'function') {
      onRejected = reason => throw reason;
    }

    return new MyPromise((resolve, reject) => {
      const dispatchCallback = (cb, arg) => {
        queueMicrotask(() => {
          try {
            const res = cb(arg);
            resolvePromise(promise, res, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };

      if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(() => {
          dispatchCallback(onFulfilled, this.value);
        });
        this.onRejectedCallbacks.push(() => {
          dispatchCallback(onRejected, this.reason);
        });
      }
      if (this.status === FULFILLED) {
        dispatchCallback(onFulfilled, this.value);
      }
      if (this.status === REJECTED) {
        dispatchCallback(onRejected, this.reason);
      }
    });
  }

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

  finally(onFinally) {
    return this.then(
      value => {
        onFinally();
        return new MyPromise(resolve => resolve(value));
      },
      reason => {
        onFinally();
        return new MyPromise((resolve, reject) => reject(reason));
      }
    );
  }
}

MyPromise.deferred = () => {
  const result = {};
  result.promise = new MyPromise((resolve, reject) => {
    result.resolve = resolve;
    result.reject = reject;
  });
  return result;
};

module.exports = MyPromise;

resolvePromise

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

function resolvePromise(promise, res, resolve, reject) {
  if (promise === res) {
    throw new TypeError('Chaining cycle detected for promise #<Promise>');
  }
  let settled = false;
  if (isObject(res)) {
    try {
      const then = res.then;
      if (typeof then === 'function') {
        then.call(
          res,
          value => {
            if (settled) {
              return;
            }
            settled = true;
            resolvePromise(promise, value, resolve, reject);
          },
          reason => {
            if (settled) {
              return;
            }
            settled = true;
            reject(reason);
          }
        );
      } else {
        resolve(res);
      }
    } catch (error) {
      if (settled) {
        return;
      }
      settled = true;
      reject(error);
    }
  } else {
    resolve(res);
  }
}

测试

sh
npm i -g promises-aplus-tests
promises-aplus-tests MyPromise