js

An Implementation based on Promises/A+

Posted by cabeza on May 10, 2019

Promises/A+规范

class Promise {
  state = "pending"; // pending,fulfilled,rejected
  value = undefined;
  callbackStack = [];
  constructor(fn) {
    fn(this.resolve.bind(this), this.reject.bind(this));
  }
  resolve(value) {
    // 2.1.2.1
    if (this.state !== "pending") return;
    this.state = "fulfilled";
    this.value = value;
    setTimeout(() => {
      this.callbackStack.forEach(fn => fn());
    }, 0);
  }
  reject(reason) {
    // 2.1.3.1
    if (this.state !== "pending") return;
    this.state = "rejected";
    this.value = reason;
    setTimeout(() => {
      this.callbackStack.forEach(fn => fn());
    }, 0);
  }
  then(onFulfilled, onRejected) {
    let self = this;
    // 穿透  2.2.7.3  2.2.7.4
    if (typeof onFulfilled !== "function") onFulfilled = x => x;
    if (typeof onRejected !== "function") {
      onRejected = x => {
        throw x;
      };
    }
    let promise2;
    promise2 = new Promise(function(resolve, reject) {
      // 2.2.4
      setTimeout(() => {
        self.handle(promise2, onFulfilled, onRejected, resolve, reject);
      });
    });
    return promise2;
  }
  handle(promise2, onFulfilled, onRejected, resolve, reject) {
    if (this.state === "pending") {
      this.callbackStack.push(() =>
        this.handle(promise2, onFulfilled, onRejected, resolve, reject)
      );
      return;
    }
    let fn = this.state === "fulfilled" ? onFulfilled : onRejected;
    let x;
    try {
      x = fn(this.value);
    } catch (e) {
      return reject(e);
    }
    this.resolvePromise(x, promise2, resolve, reject);
  }
  resolvePromise(x, promise2, resolve, reject) {
    let self = this;
    let called = false;
    if (x === promise2) return reject(new TypeError("TypeError"));
    try {
      // 2.3.2
      if (x instanceof Promise) {
        x.then(resolve, reject);
        return;
      }
      if (x !== null && (typeof x === "object" || typeof x === "function")) {
        let then = x.then;
        if (typeof then === "function") {
          return then.call(
            x,
            function(v) {
              if (called) return;
              called = true;
              //2.3.3.3.1
              self.resolvePromise(v, promise2, resolve, reject);
            },
            function(y) {
              if (called) return;
              called = true;
              reject(y);
            }
          );
        }
      }
      resolve(x);
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  }
}

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