Delayable JavaScript Tasks

I’m customizing a system that uses ExtJS framework, and for one reason or another, one of the components fires multiple events after it’s been updated with new data. My job here was to reload the data for the second component as the first got its data. What happened was that I got four (4) updated events and this caused some issues, since loading the data for the second compoent was really expensive. I needed a way to delay the reloading of the second data so that it wouldn’t send off four Ajax requests, but only one.

The updated events I was listening for, typically fired within 300-600 ms. While reloading my data was expensive, it was also fast and a delay was acceptable. The solution to the problem we went for is the one below.

/**
 * Allows scheduling the execution of a function in the future. 
 * Subsequent calls with the same <code>name</code> value will cancel
 * the first scheduled execution, if it hasn't executed already.
 */
var DelayableExecution1 = function(){
  var timeouts = {};

  var executionWrapper = function(name, fn) {
    return function() {
      delete timeouts[name];
      fn();
    };
  };

  return function(name, fn, delay) {
    var timeout = timeouts[name];
    if (timeout) {
      clearTimeout(timeout);
    }
    timeouts[name] = setTimeout(executionWrapper(name, fn), delay);
  };
}();

var DelayableExecution2 = function() {
  var timeouts = {};
  var delayedExecutionCounter = 0;

  var wrappedExecution = function(name, fn, counterWhenScheduled) {
    return function() {
      if (counterWhenScheduled < timeouts[name]) {
        // a later execution has been scheduled, skipping
        return;
      }
      delete timeouts[name];
      fn();
    };
  };

  return function(name, fn, delay) {
    var thisScheduling = delayedExecutionCounter++;
    timeouts[name] = thisScheduling;
    setTimeout(wrappedExecution(name, fn, thisScheduling), delay);
  };
}();
// and to delay the ajaxCall with 600 ms, one would do something like this:
var runLater = DelayableExecution();
var ajaxCall = function() { ... };
runLater('update', ajaxCall, 600);

As you see, two different alternatives with more or less the same feature set. A third option would be to use a setInterval which executes the function fn as soon as it’s delay-to-point-in-time has been reached. This value could easily be increased as new calls comes in.

This work by Fredrik Wendt is licensed under CC by-sa.