Promise to realize

Please go to nodeJS 4.3.2, don’t ask me why I always use this, it’s really good!!

Promise specification

There’s A whole bunch of promise specifications, but ES6 uses the Promise A+ specification

The documentation Promise A+ specification links to A friendly explanation of the specification in English

I believe that you have read the link above to the entire promise implementation process have an understanding. So to summarize this

  • With the THEN method, the THEN () method continues to return the Promise object to implement the chain call.
  • The Promise operation will only be in one of three states: Pending, Fulfilled and Rejected.
  • The Promise state can only be changed from an incomplete state to a completed state or a failed state, and cannot be reversed. The completed and failed states cannot convert to each other.
  • Once the state of a Promise is changed, it cannot be changed.
  • Accept completed and error callback methods. When the operation completes or an error occurs, the corresponding method is called

implementation

The following is the actual (Chao) and (XI) of vegetable dog according to the specification. Welcome to point out any mistakes.

Bosses package the promise implementation test tool, there is a complete implementation, can use this test is consistent with the standard.

For more examples of what the big boys have packaged, you can see it here. The implementation of the vegetable dog reference which belofte.js for easy understanding (is lazy) cut many types of judgment.

Notes to write more than I do, speak more than I detailed an article

const PENDING = "Pending";
const FULFILLED = "Fulfilled";
const REJECTED = "Rejected";

const root = typeof window= = ="undefined" ? global : window;
const isFunction = data= > typeof data === "function";
const isMyPromise = fn= > fn instanceof MyPromise;
const isObject = data= >
  data && (typeof data === "object" || typeof data === "function");
// Since promises are microtasks, we simulate them as best we can
const nextTick = (function() {
  if (typeof root.process === "object" && isFunction(root.process.nextTick)) {
    / / the node environment
    return function(fn) {
      // process.nextTick is a microtask
      root.process.nextTick(fn);
    };
  } else {
    // Browser environment
    return function(fn) {
      // setTimeout is a macro task
      root.setTimeout(fn, 0);
    };
  }
})();

// Change the promise from the intermediate state to the final state
const promiseResolutionProcedure = function(promise, result, async = true) {
  if (promise === result) {
    /** * because promise will wait for its result if the value received is a promise. * So if the value received is itself recursive. @ see https://promisesaplus.com/ * * * / under article 2.3.1
    promise._reject(new TypeError("Chaining cycle detected for promise"));
    return;
  }
  // If a promise is received
  if (isMyPromise(result)) {
    switch (result._state) {
      case FULFILLED: {
        nextTick(function() {
          promise._resolve(result._value);
        });
        break;
      }
      case REJECTED: {
        nextTick(function() {
          promise._reject(result._reason);
        });
        break;
      }
      case PENDING: {
        const _resolve = result._resolve;
        const _reject = result._reject;

        result._resolve = function(value) {
          _resolve.call(result, value);
          promise._resolve(value);
        }.bind(result);

        result._reject = function(reason) {
          _reject.call(result, reason);
          promise._reject(reason);
        }.bind(result);
        break; }}return;
  }

  // If a thenable object is received
  if (isObject(result) && isFunction(result.then)) {
    / * * * * * @ multiple calls only effective for the first time see https://promisesaplus.com/ article 2.3.3.3.3 * /
    let flag = false;
    const _resolve = function(value) {
      if (flag) {
        return;
      }
      flag = true;
      promiseResolutionProcedure(promise, value);
    };
    const _reject = function(reason) {
      if (flag) {
        return;
      }
      flag = true;
      promise._reject(reason);
    };
    const thenTemp = function() {
      try {
        result.then(_resolve, _reject);
      } catch(error) { _reject(error); }};if (async) {
      nextTick(thenTemp);
    } else {
      thenTemp();
    }
    return;
  }

  promise._resolve(result);
  return;
};

class MyPromise {
  constructor(resolver) {
    if(! isFunction(resolver)) {throw new TypeError("Promise resolver undefined is not a function");
    }
    /** @type { PENDING | FULFILLED | REJECTED} */
    this._state = PENDING;

    this._value = undefined;
    this._reason = undefined;

    this._isPromise = true;
    /** * because the same promise can be then more than once. * Multiple does not refer to the chain call !!!! TAT */ 
    this._resolveFnQueues = [];
    this._rejectFnQueuse = [];

    promiseResolutionProcedure(this, { then: resolver }, false);
  }

  _resolve(value) {
    if (this._state ! == PENDING) {return;
    }
    this._state = FULFILLED;
    this._value = value;
    if (this._resolveFnQueues.length) {
      nextTick((a)= > {
        this._resolveFnQueues.forEach(cb= > cb(value));
        this._resolveFnQueues.length = 0;
        this._rejectFnQueuse.length = 0;
      });
    }
  }

  _reject(reason) {
    if (this._state ! == PENDING) {return;
    }
    this._state = FULFILLED;
    this._reason = reason;
    if (this._rejectFnQueuse.length) {
      nextTick((a)= > {
        this._rejectFnQueuse.forEach(cb= > cb(reason));
        this._resolveFnQueues.length = 0;
        this._rejectFnQueuse.length = 0; }); }}// Then register a listener, in this promise onFulfilled or onRejected
  then(onFulfilled, onRejected) {
    onFulfilled = isFunction(onFulfilled) ? onFulfilled : MyPromise.resolve;
    onRejected = isFunction(onRejected) ? onRejected : MyPromise.reject;
    const chainPromise = new MyPromise(function() {});

    const nextOnFulfilled = function(value) {
      let result;
      try {
        result = onFulfilled(value);
        promiseResolutionProcedure(chainPromise, result);
      } catch(error) { chainPromise._reject(error); }};const nextOnRejected = function(reason) {
      let result;
      try {
        result = onRejected(reason);
        promiseResolutionProcedure(chainPromise, result);
      } catch(error) { chainPromise._reject(error); }};switch (this._state) {
      case FULFILLED: {
        nextTick((a)= > {
          nextOnFulfilled(this._value);
        });
        break;
      }
      case REJECTED: {
        nextTick((a)= > {
          nextOnRejected(this._reason);
        });
        break;
      }
      case PENDING: {
        this._resolveFnQueues.push(nextOnFulfilled);
        this._rejectFnQueuse.push(nextOnRejected); }}return chainPromise;
  }

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

  toString() {
    switch (this._state) {
      case PENDING:
        return "Promise { <pending> }";
      case FULFILLED:
        return "Promise { " + this._value + "}";
      case REJECTED:
        return "Promise { <rejected> " + this._reason + "}"; }}static resolve(value) {
    return new MyPromise(resolve= > resolve(value));
  }

  static reject() {
    return new MyPromise((resolve, reject) = >reject(value)); }}Copy the code

supplement

Async implementation

Async is essentially a Generator and an autoexecutor wrapped in a function

Ruan yifeng big article about the fried chicken clear. The content here also comes from that article, I also review the realization.

// async
async function fn(args){
  // ...
}

/ / is equivalent to
function fn(args){ 
  return spawn(function* () {
    // ...
  }); 
}
// The implementation of spawn
function spawn(genF) {
  return new Promise(function(resolve, reject) {
    var gen = genF();
    step(function() { return gen.next(undefined); });
    function step(nextF) {
      try {
        var next = nextF();
      } catch(e) {
        return reject(e); 
      }
      if(next.done) {
        return resolve(next.value);
      } 
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });      
      }, function(e) {
        step(function() { returngen.throw(e); }); }); }}); }Copy the code