Home Reference Source Repository

es6/Services/Loop.js

import Promise    from 'bluebird'
import Expressive from '../utils/Expressive'
import * as Time  from '../utils/Time'
import Events     from '../utils/Events' 

/**
 * a basic polling Loop which rejects actions until the previous action has completed
 * 
 * @class Loop
 */
export default class Loop extends Expressive {
  /**
   * Bubbles async errors into the main process so we can kill this Wyst node if something is awry
   *
   * @method     bubble
   * @param      {Error}  err     The error to throw
   */
  static bubble (err) {
    throw err
    process.exit(1)
  }

  /**
   * constructor
   *
   * @method     constructor
   */
  constructor (config = {}) {
    super()
    this.interval = config.interval || Time.seconds(1)
    this._paused  = true
    return Promise.resolve(this)
  }

  /**
   * starts Polling
   *
   * @method     start
   */
  start () {
    this.togglePaused()
    this.$ = setInterval( this.tick.bind(this), this.interval )
    return this
  }

  /**
   * stops Polling
   *
   * @method     pause
   */
  pause () {
    this.togglePaused()
    clearInterval(this.$)
    return this
  }

  /**
   * determines if we are paused or polling
   *
   * @method     togglePaused
   */
  togglePaused () {
    this._paused = this._paused ? false : true
    return this
  }

  /**
   * Determine if blocked.
   * Prevents stacking polls if there is some external slowness
   *
   * @method     isBlocked
   * @return     {boolean}  True if blocked, False otherwise.
   */
  get isBlocked () {
    return this._blocked || false
  }

  /**
   * Toggles if we should block on the next polling cycle
   *
   * @method     toggleBlocked
   */
  toggleBlocked () {
    this._blocked = this._blocked ? false : true
    return this
  }

  /**
   * kills the Loop
   * 
   * @method kill
   */
  kill () {
    this.pause()
    this.removeAllListeners()
  }
  
  /**
   * Main polling loop handler
   *
   * @method     tick
   */
  tick () {
    // Prevent stacking operations if RethinkDB cluster is under stress
    if ( this.isBlocked ) return
    this.toggleBlocked()

    this.emitTick((function unblock () {
      this.toggleBlocked()
    }).bind(this))
  }
}

Loop.Event = {
    down : 'loop:down'
  , up   : 'loop:up'
  , tick : 'loop:tick'
}

Events.methodize( Loop, Loop.Event )