utils.js

// @flow

/**
 * Deferred is modeled after jQuery's deferred object. It inverts a promise 
 * such that its resolve and reject methods can be invoked without wrapping 
 * all of the related code within a Promise's function. 
 *
 * @class Deferred
 */
export class Deferred {
  /**
   * Creates an object with four properties of note; promise, resolve, reject 
   * and a flag complete that will be set once either resolve or reject have 
   * been called. A Deferred is considered to be pending while complete is set
   * to false.
   *
   * Once constructed, resolve and reject can be called later, at which point, 
   * the promise is completed. The promise property is the promise resolved 
   * or rejected by the associated properties and can be used with other 
   * async/await or Promise based code.
   *
   * @instance
   * @memberof Deferred
   * @method ⎆⠀constructor
   * 
   * @param {any} resolveWith a deferred resolved as Promise.resolve() might do
   * @param {any} rejectWith a deferred rejected as Promise.reject() might do
   */
  constructor(resolveWith: any, rejectWith: any) {
    this.promise = new Promise((resolve, reject) => {
      this.complete = false;
      
      this.resolve = (...args) => { 
        this.complete = true; 
        return resolve(...args); 
      };
      
      this.reject = (...args) => {
        this.complete = true;
        return reject(...args);
      };
      
      if (resolveWith && !rejectWith) { this.resolve(resolveWith) }
      if (rejectWith && !resolveWith) { this.reject(rejectWith) }
    });
  }
  
  /**
   * Shorthand getter that denotes true if the deferred is not yet complete. 
   *
   * @instance
   * @memberof Deferred
   * @method ⬇︎⠀pending
   * 
   * @return {boolean} true if the promise is not yet complete; false otherwise
   */
  get pending(): boolean { return !this.complete }
}