Source: lib/poller.js

/**
 * @module poller
 * @description
 * A Poller for Endpoints
 */


"use strict";


// built-in modules
var events = require("events");
var util = require("util");


// npm-installed modules
var _ = require("lodash");


// own modules
var utils = require("./utils");


/**
 * Poller Class. Inherits from events.EventEmitter. This poller
 * is designed in that it polls for new items automatically
 * without having you implement `lastreadId` logic.
 *
 * @example
 * // create a new poller that keeps retrieiving traffic updates
 * var poller = new sdk.Poller(sdk.trafficUpdates.get, {
 *   interval: 5000, // 5 seconds
 * });
 *
 * // listen for new updates
 * poller.on("message", function(updates, meta, responseObject) {
 *   console.log("received these updates: %j", updates);
 * });
 *
 * // listen for errors e.g. network failures etc.
 * // if an error occurs an you not listening on the "error"
 * // event, the error will bubble up to the domain/process.
 * poller.on("error", function(err) {
 *   console.log("error: %s", err);
 * });
 *
 * // you have to explicitly start it
 * poller.start();
 *
 * // lets say we close it after a minute or so
 * setTimeout(function() {
 *   poller.stop();
 * }, 1000 * 60);
 *
 * @constructor
 * @param  {itemsGetRequest} getRequest - request function fired in each poll
 * @param  {Object} [options]
 * @param  {Object|Function} [options.params] - parameters passed to get request.
 *      If `options.params` is a function, it is invoked and its return value is
 *      assumed an object as the request params.
 *      If `options.params` requires to do an asynchronous action, it is passed a
 *      `done` function as its only argument to call with the value when done.
 * @param  {Integer} [options.interval=5000] - configures the poller's timer
 */
function Poller(getRequest, options) {
    options = options || { };
    events.EventEmitter.call(this);
    this._pollerOptions = utils.getPollerOptions([utils.setup().poller, options]);
    this._get = getRequest;
    this._params = options.params || { };
    this._lastreadId = this._params.lastreadId || null;
    this._timer = null;
    this._requestPending = false;
    this._paused = false;
    return this;
}


// inherit from the EventEmitter class
util.inherits(Poller, events.EventEmitter);


/**
 * Starts the poller.
 */
Poller.prototype.start = function start() {
    var me = this;
    function sendRequest(args) {
        me._get(args, function(err, items, meta, res) {
            me._requestPending = false;

            if (err) {
                return me.emit("error", err);
            }
            // emit 'message' event if we received items
            if (items.length) {
                // since we do not know if we are in ascending or descending
                // order, please do check
                var id1 = items[0].id;
                var id2 = items[items.length - 1].id;
                me._params.lastreadId = id1 > id2 ? id1 : id2;
                return me.emit("message", items, meta, res);
            }
            return null;
        });
    }
    me._timer = setInterval(function() {
        // if we are paused, return immediately
        if (me._paused) {
            return null;
        }

        // only send request, if there is none pending
        if (me._requestPending) {
            return null;
        }

        me._requestPending = true;
        var args = { };
        if (me._lastreadId) {
            args.lastreadId = me._lastreadId;
        }
        if (_.isFunction(me._params)) {
            if (me._params.length) {
                return me._params(function(params) {
                    _.merge(args, params);
                    return sendRequest(args);
                });
            }
            _.merge(args, me._params());
        } else {
            _.merge(args, me._params);
        }
        return sendRequest(args);
    }, me._pollerOptions.interval);
};


/**
 * Pause the poller. Note that this does not actually stop the
 * interval used internally. It basically causes a 'noop' to be
 * fired instead of an actual network request. This implementation
 * is simple, and also cheap if you require to pause and resume
 * multiple times.
 */
Poller.prototype.pause = function pause() {
    this._paused = true;
    return this;
};


/**
 * Resume the poller, if it was paused.
 */
Poller.prototype.resume = function resume() {
    this._paused = false;
    return this;
};


/**
 * Stops the poller.
 */
Poller.prototype.stop = function stop() {
    this._timer.close();
    this.removeAllListeners();
    return this;
};



// exporting the constructor
exports = module.exports = Poller;
exports.Poller = Poller;