Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

手写一个符合Promises/A+规范的promise #119

Open
Cosen95 opened this issue Apr 28, 2020 · 1 comment
Open

手写一个符合Promises/A+规范的promise #119

Cosen95 opened this issue Apr 28, 2020 · 1 comment
Labels

Comments

@Cosen95
Copy link
Owner

Cosen95 commented Apr 28, 2020

No description provided.

@Cosen95
Copy link
Owner Author

Cosen95 commented Sep 10, 2020

具体实现讲解可参考这里

const PENDING = "PENDING";
const RESOLVED = "RESOLVED";
const REJECTED = "REJECTED";

const resolvePromise = (promise2, x, resolve, reject) => {
  console.log(promise2);
  if (promise2 === x) {
    // # 2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
    return reject(
      new TypeError("Chaining cycle detected for promise #<Promise>")
    );
  }

  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    // 防止多次调用成功和失败
    // # 2.3.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
    let called;
    try {
      // 取x的then属性,如果是一个函数,认定其为promise
      let then = x.then;
      if (typeof then === "function") {
        // # 2.3.3.3 If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise
        then.call(
          x,
          (y) => {
            // # 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
            // 采用promise的成功结果将值向下传递
            // 这里的y有可能还是一个promise,所以递归调用resolvePromise
            if (called) {
              return;
            }
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            // # 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r
            // 采用失败结果向下传递
            if (called) {
              return;
            }
            called = true;
            reject(r);
          }
        );
      } else {
        // 说明x是一个普通对象
        resolve(x);
      }
    } catch (e) {
      if (called) {
        return;
      }
      called = true;
      reject(e);
    }
  } else {
    // x是一个普通值
    // # 2.3.4 If x is not an object or function, fulfill promise with x
    resolve(x);
  }
};

class Promise {
  constructor(executor) {
    this.status = "PENDING"; // 默认pending状态
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value;
        this.status = RESOLVED;
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };

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

    try {
      executor(resolve, reject); // 默认执行器会立即执行
    } catch (error) {
      reject(e);
    }
  }
  then(onfulfilled, onrejected) {
    // onfulfilled onrejected是可选参数
    // # 2.2.1 Both onFulfilled and onRejected are optional arguments:
    onfulfilled =
      typeof onfulfilled === "function" ? onfulfilled : (data) => data;
    onrejected =
      typeof onrejected === "function"
        ? onrejected
        : (err) => {
            throw err;
          };
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === RESOLVED) {
        setTimeout(() => {
          try {
            // # 2.2.2 If onFulfilled is a function:
            //    # 2.2.2.1 it must be called after promise is fulfilled, with promise’s value as its first argument.
            let x = onfulfilled(this.value);
            // x可能是普通值 也可能是promise
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            // # 2.2.3 If onRejected is a function,
            //    # 2.2.3.1 it must be called after promise is rejected, with promise’s reason as its first argument.
            let x = onrejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onfulfilled(this.value);
              // x可能是普通值 也可能是promise
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onrejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return promise2;
  }
}

// resolve方法
Promise.resolve = function (val) {
  return new Promise((resolve, reject) => {
    resolve(val);
  });
};

// reject方法
Promise.reject = function (val) {
  return new Promise((resolve, reject) => {
    reject(val);
  });
};

// race方法
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(resolve, reject);
    }
  });
};

// all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)

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

module.exports = Promise;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant