API Docs for: 0.0.1
Show:

File: src/utils/lib/timers.js

/**
 * Collection of various utility functions.
 *
 *
 * <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
 * New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
 *
 * @module utils
 * @class Utils
 * @static
*/


"use strict";

require('polyfill/polyfill-base.js');

var NAME = '[utils-timers]: ',
    _asynchronizer, _async;

/**
 * Forces a function to be run asynchronously, but as fast as possible. In Node.js
 * this is achieved using `setImmediate` or `process.nextTick`.
 *
 * @method _asynchronizer
 * @param callbackFn {Function} The function to call asynchronously
 * @static
 * @private
**/
_asynchronizer = (typeof setImmediate !== 'undefined') ? function (fn) {setImmediate(fn);} :
                    ((typeof process !== 'undefined') && process.nextTick) ? process.nextTick : function (fn) {setTimeout(fn, 0);};

/**
 * Invokes the callbackFn once in the next turn of the JavaScript event loop. If the function
 * requires a specific execution context or arguments, wrap it with Function.bind.
 *
 * I.async returns an object with a cancel method.  If the cancel method is
 * called before the callback function, the callback function won't be called.
 *
 * @method async
 * @param {Function} callbackFn
 * @param [invokeAfterFn=true] {boolean} set to false to prevent the _afterSyncFn to be invoked
 * @return {Object} An object with a cancel method.  If the cancel method is
 * called before the callback function, the callback function won't be called.
**/
_async = function (callbackFn, invokeAfterFn) {
    console.log(NAME, 'async');
    var canceled;

    invokeAfterFn = (typeof invokeAfterFn === 'boolean') ? invokeAfterFn : true;
    (typeof callbackFn==='function') && _asynchronizer(function () {
        if (!canceled) {
            callbackFn();
        }
    });

    return {
        cancel: function () {
            canceled = true;
        }
    };
};

/**
 * Invokes the callbackFn once in the next turn of the JavaScript event loop. If the function
 * requires a specific execution context or arguments, wrap it with Function.bind.
 *
 * I.async returns an object with a cancel method.  If the cancel method is
 * called before the callback function, the callback function won't be called.
 *
 * @method async
 * @param {Function} callbackFn
 * @param [invokeAfterFn=true] {boolean} set to false to prevent the _afterSyncFn to be invoked
 * @return {Object} An object with a cancel method.  If the cancel method is
 * called before the callback function, the callback function won't be called.
**/
module.exports.async = _async;

/**
 * Invokes the callbackFn after a timeout (asynchronous). If the function
 * requires a specific execution context or arguments, wrap it with Function.bind.
 *
 * To invoke the callback function periodic, set 'periodic' either 'true', or specify a second timeout.
 * If number, then periodic is considered 'true' but with a perdiod defined by 'periodic',
 * which means: the first timer executes after 'timeout' and next timers after 'period'.
 *
 * I.later returns an object with a cancel method.  If the cancel() method is
 * called before the callback function, the callback function won't be called.
 *
 * @method later
 * @param callbackFn {Function} the function to execute.
 * @param [timeout] {Number} the number of milliseconds to wait until the callbackFn is executed.
 * when not set, the callback function is invoked once in the next turn of the JavaScript event loop.
 * @param [periodic] {boolean|Number} if true, executes continuously at supplied, if number, then periodic is considered 'true' but with a perdiod
 * defined by 'periodic', which means: the first timer executes after 'timeout' and next timers after 'period'.
 * The interval executes until canceled.
 * @return {object} a timer object. Call the cancel() method on this object to stop the timer.
*/
module.exports.later = function (callbackFn, timeout, periodic) {
    console.log(NAME, 'later --> timeout: '+timeout+'ms | periodic: '+periodic);
    var canceled = false;
    if (typeof timeout!=='number') {
        return _async(callbackFn);
    }
    var wrapper = function() {
            // nodejs may execute a callback, so in order to preserve
            // the cancel() === no more runny-run, we have to build in an extra conditional
            if (!canceled) {
                callbackFn();
                // we are NOT using setInterval, because that leads to problems when the callback
                // lasts longer than the interval. Instead, we use the interval as inbetween-phase
                // between the separate callbacks.
                id = periodic ? setTimeout(wrapper, (typeof periodic==='number') ? periodic : timeout) : null;
            }
        },
        id;
    (typeof callbackFn==='function') && (id=setTimeout(wrapper, timeout));

    return {
        cancel: function() {
            canceled = true;
            id && clearTimeout(id);
            // break closure:
            id = null;
        }
    };
};