Defer Function Execution Until Variables Are Available

I recently wrote this simple function, which hijacks a function attached to an object, automatically deferring calls to it (in the order received), until an availability function returns true. I find this to be useful in large applications with many dependencies, especially third-party ones, where it can be difficult to know that the variables/functions used by a function exist. And rather than check for the variables and handle function deferment manually, the function in todays article will manage deferment automatically for you.

Getting ready

To start, here is an example function that you might want to defer. The following function modifies a value, then executes a global function, _myGlobalFunction:

var myNamespace = {
	updateMyGlobalVariable(value) {
		// do something to value
		_myGlobalFunction(newvalue);
	}
};

This function would throw an exception if the _myGlobalFunction function was not defined when it executed.

How to do it…

The function to attach a deferment is:

/**
 * This function causes the target function to queue all calls to it until readyFx return true.
 * @param namespace {Object} Required. The namespace object.
 * @param fx {String} Required. The function name on namespace.
 * @param readyFx {Function} Required. The evaluation function.
 */
function defermentFunction(namespace, fx, readyFx) {
	var ofx = namespace[fx], // store pointer to old function
		queue = [],
		intervalId, i= 0,
		done = function() {
			clearInterval(intervalId);
			namespace[fx] = ofx; // restore function

			for (var m=0, n=queue.length-1; m<<n; m+=1) {
				ofx.apply(namespace, queue[m]);
			}
		};

	namespace[fx] = function() {
		var args = arguments;
		queue.push(args);

		if (readyFx(args)) {
			done();
		}
		else if (! (intervalId)) {
			// when done, process all queued requests
			intervalId = setInterval(function() {
				console.log(i++);
				if (readyFx(args)) {done();}
			}, 250);
		}
	}
}

And you can use the deferment function as follows:

defermentFunction(myNamespace, updateMyGlobalVariable, function(args) {
	return window._myGlobalFunction;
});

You can defer execution anyway you want, you could even use this function to check if something in the DOM is available, before executing a function:

defermentFunction(myNamespace, myDomFunction, function(args) {
	return YAHOO.util.Dom.get(myDomNodeId);
});

How it works…

The deferment function takes the existing function, in this example myNamespace.updateMyGlobalVariable, turning it into a local variable, before replacing it with a new function. The new function evaluates the availability function. When the availability function returns false, the variables are added to a queue and an interval timer begins the first time the new function is executed. The timer keeps executing until the availability function returns true. When that happens the original function is restored and executed with the variables stored in the queue.

Theres more…

The biggest limitation of the deferment function is that it can only defer functions attached to an object. Since the deferment function is global it does not create a closure with local variables in an execution context, so therefore it cannot reference them. This is why the first variable is a namespace object, and the second argument is the string name of the function attached to that namespace object. You might be able to hack around this limitation by using the scope chain, but this solution was sufficient enough for my needs.