Feeds:
Posts
Comments

Archive for the ‘Closure’ Category

JQuery recently(*) introduced a handy, built-in mechanism for dealing with asynchronous processes in a neat and easy-to-read format. These are “Deferred” objects. They take an asynch call and process a ‘callback’ function when the asynch call returns. If you’ve ever made an Ajax call using JQuery, you’ve probably used these:

var jqxhr = $.ajax
	.get(url, searchdata)
	.success(function(data) {
		processdata(data);
	})
	.error(function(msg) {
		displayerror(msg);
	});

In this example, we’re going to send the ‘searchdata’ to the web service at ‘url’ and get data back. When the data comes back, either the success or the error function will be called. And, all this happens automatically!

The mechanism that manages this is the Deferred object. Under the covers, the Deferred objects manage a queue of functions that are called in turn. When one completes, the next is called. These are very handy for UI animations, Web Service calls, managing user input and anything else that requires a break to allow the browser to continue processing while something else happens in the background. It’s also a nifty mechanism to ensure that functions are called in sequence, even though they may take some time to complete.

To make the magic happen, functions that require deferred resolution need to create a Deferred object and return its’ ‘promise’ object. When the function finally completes its task, the function calls either the ‘resolve()’ or ‘reject()’ method of the Deferred object, and this starts up the next function in the queue. Note that the ‘resolve’ call will execute the function passed to the ‘success’ or ‘done’ method of the promise object, while ‘reject’ will call the function passed to ‘error’ or ‘fail’. (JQuery is changing the method names to the more generic ‘done’ and ‘fail’).

Here’s An example of a function that incrementally changes a CSS property using a timer.

// Increase a css property from a value of 'current' to 'target'
function doit() {
	var current = parseInt($("#mydiv").css("margin-top"));

	// update the 'margin-top' value every 30 milliseconds until done
	var t = setInterval(function () {
		$("#mydiv").css("margin-top", current++);

		// if we're done, stop 
		if (current == target) {
			clearInterval(t);
			return;
		};
	}, 30);
};

doit();
needstobecalledafterdoit();

The anonymous function passed into the setInterval call will be executed once every 30 milliseconds. This will continue until the current value of the “margin-top” CSS property is equal to the target. The ‘setInterval’ timer operates asynchronously; only the setup happens when doit() is called. You can easily see that you could not simply call doit() and then expect the function to complete before the next statement. You need a way to hold off on calling needstobecalledafterdoit() until doit() really is complete. Here’s the version using a Deferred object to make sure that needstobecalledafterdoit() is called after doit() is complete

// Increase a css property from a value of 'current' to 'target'
function doit() }
	var dfd = $.Deferred();
	var current = parseInt($("#mydiv").css("margin-top"));

	// update the 'margin-top' value every 30 milliseconds until done
	var t = setInterval(function () {
		$("#mydiv").css("margin-top", current++);

		// if we're done, stop 
		if (current == target) {
			clearInterval(t);
			dfd.resolve();
			return;
		};
	}, 30);
	return dfd.promise();
}

doit()
.done(function() {
	needstobecalledafterdoit();
});

This works as advertised, and needstobecalledafterdoit() is called at the right time.

Asynch programming like this does introduce some architectural issues. Here’s an other example that, by the way, doesn’t work.

for (var i = 0; i < 4; i++) {
	doit()
	.done( function () {
		callme(i);
	});
};

So what’s wrong here? The function ‘callme(i)’ will be executed 4 times, and each time the value of ‘i’ will be 4. What’s happening is that, in order for the execution of callme() to use the value of ‘i’, JavaScript needs to save ‘i’ off somewhere so it can be used when doit() is finished. Saving of variable references like this is called a ‘Closure’. These are used all the time in javascript, and you’ve probably used them and just thought of it as javascript ‘magic’. What’s really happening is that when you use a value such as i within an inline or anonymous function, javascript hangs on to a copy of that variable so it can resolve uses of it when you need them. In the earlier example, the value of ‘t’, ‘current’, and ‘dfd’ were available to the function we passed to setInterval() through a closure.

So, again we ask, what’s wrong with this example and why is ‘i’ always 4? It turns out that javascript creates the closure when the function is executed. A closure is an internal construct that keeps copies to all the variables that a function needs when it executes. When our ‘callme(i)’ function executes, it uses a reference to the variable ‘i’ declared in the for loop to call callme(). JavaScript dutifully keeps a reference to ‘i’ around and when the function is executed, a closure is created using the value that ‘i’ has at that moment. The problem is, that by the time doit() is completed and the ‘done’ function is executed, our loop has completed as well and ‘i’ is now equal to 4.

How to get around this? The trick is to give javascript a function to execute so that it will create a closure while the loop is processing:

for (var i = 0; i < mylist.length; i++) {
	(function(saved_i) {
		doit()
		.done( function () {
			callme(saved_i);
		});
	})(i);
};

The syntax (function(i) {})(i); creates and executes a function in-line. Remember, the closure for this function is created at execution time and JavaScript uses value that i has at the time the closure is created – in this case, while the loop is still processing processing. In the example, I’ve used ‘saved_i’ to highlight this.

Deferreds and Closures are more than a bit tricky to get your head around, and there are a lot of resources on the web to help. It’s important that you understand them and their uses because they really are the foundation of modern browser application interfaces.

(*) N.B. ‘recently’ is a relative term! 😉

Read Full Post »