At times you may need to use a polling function in order to wait for an event before the rest of your code can fire. One way we can solve this is with a polling function:


/**
 * @license JS Polling - By Chris West - MIT License
 */
(function(emptyObj, emptyArr, types, undefined) {
  function typeOf(obj) {
    return obj == undefined
      ? obj === undefined ? 'undefined' : 'null'
      : emptyObj.toString.call(obj).slice(8, -1);
  }
  
  /**
   * Polls every so often until a specific condition is true before running the
   * fnOnReady function.
   * @param {!function():boolean} fnIsReady  The function that will be executed
   *     every time the amount of milliseconds (interval) specified passes.
   *     This function should return true if the fnOnReady parameter is ready to
   *     be executed, otherwise false should be returned.  The parameters passed
   *     to this function will be those specified as args.
   * @param {!Function} fnOnReady  The function to be run once the polling has
   *     ceased without a timeout.  The parameters passed to this function will
   *     be those specified as args.
   * @param {?Function=} fnOnTimeout  The optional function to be run once the
   *     polling ceased due to the timeout limit being reached.  The parameters
   *     passed to this function will be those specified as args.
   * @param {?number=} interval  The optional amount of milliseconds to wait
   *     before executing the fnIsReady function again.  If not specified, this
   *     interval will default to 50.
   * @param {?number=} timeout  The optional amount of milliseconds to wait
   *     until the polling should stop.  This doesn't include the amount of time
   *     used to run fnIsReady.  Defaults to infinity if not specified.
   * @param {?Array=} args  The optional array of arguments to send to the
   *     functions:  fnIsReady, fnOnReady, and fnOnTimeout.
   */
  poll = function(fnIsReady, fnOnReady, fnOnTimeout, interval, timeout, args) {
    // Pre-process the arguments to account for optionals.
    var myArg, myArgs = emptyArr.slice.call(arguments, 2);
    var i = -1;
    while(myArg = myArgs[++i]) {
      if(myArg != undefined && typeOf(myArg) != types[i]) {
        myArgs.splice(i, 0, undefined);
      }
    }
    fnOnTimeout = myArgs[0];
    interval = myArgs[1] || 50;
    timeout = myArgs[2] || Infinity;
    args = myArgs[3] || emptyArr;
    
    function fnCaller() {
      var me = this;
      
      if(fnIsReady.apply(me, args)) {
        fnOnReady.apply(me, args);
      }
      else if((timeout -= interval) > 0) {
        setTimeout(fnCaller, interval);
      }
      else if(fnOnTimeout) {
        fnOnTimeout.apply(me, args);
      }
    }
    fnCaller();
  }
})({}, [], ['Function', 'Number', 'Number', 'Array']);

Let’s say that you want a function which tells the user when window.myCustomVar is defined. The following will do just that:


poll(
  function() {
    return 'myCustomVar' in window;
  },
  function() {
    alert('"myCustomVar" is in the global namespace.');
  }
);

Now, if we were to run the following code in the same session, the message “myCustomVar” is in the global namespace would be alerted to the user:


window.myCustomVar = "something";

To tell you the truth the Closure Compiler annotation pretty much sums up how to use the function to the full so feel free to use this function in your own projects. 8)

Categories: BlogJavaScript

Leave a Reply

Your email address will not be published. Required fields are marked *