Manual Reference Source Test

src/modules/observable.js

/**
 * Minimal observable class
 */
class Observable {
  /**
   * Observable constructor
   */
  constructor() {
    /**
     * Default observers class variable
     *
     * @type {array}
     */
    this._observers = [];
  }

  /**
   * Register callback to be called with events of specified type
   *
   * Callback will be called with an argument depending on the type of event.
   *
   * @example
   * const observable = new Observable();
   * const off = observable.on('foo', (data) => {
   *   console.log(`Foo!`, data);
   * });
   *
   * // To stop observing events at a later time:
   * off();
   *
   * @param {string} type - Event type
   * @param {Function} callback - Event callback function
   * @return {Function} - Function to be called to unregister this callback
   */
  on(type, callback) {
    if (typeof type !== 'string') {
      throw new Error('Type argument passed to Observable#on() is not specified or not a string');
    }
    if (typeof callback !== 'function') {
      throw new Error('Callback function passed to Observable#on() is not specified or not a function');
    }

    if (!this._observers[type]) {
      this._observers[type] = [];
    }

    if (this._observers[type].indexOf(callback) === -1) {
      this._observers[type].push(callback);
    }

    return () => {
      const typeObservers = this._observers[type];
      const index = typeObservers.indexOf(callback);
      if (index >= 0) {
        typeObservers.splice(index, 1);
      }
    };
  }

  /**
   * Notify all observers of specified event type
   *
   * @param {string} type - Event type
   * @param {*} data - Event data
   */
  notify(type, data) {
    if (Array.isArray(this._observers[type])) {
      this._observers[type].forEach((callback) => {
        try {
          callback(data);
        } catch (error) {
          // TODO: For now log exception in console log, but consider elevating Logger property from
          // Identity to Observable.
          // eslint-disable-next-line no-console
          console.log(error);
        }
      });
    }
  }

  // TODO: Also add setValue etc. to provide an easy standard way of monitoring instance property values?
  // setValue(key, value, notify = (this[key] !== value)) {
  //   this[key] = value;
  //   if (notify) {
  //     this.notify(key, value);
  //   }
  // }
}

export default Observable;