JavaScript: a single-threaded language with many asynchronous calls
JavaScript is a single-threaded language: it means that at a given time, only one script is being executed but a lot of things are asynchronous: you call a function that has to do some work and that function returns immediately. The best example would be the ajax calls: you send the request, and the browser returns immediately, executing what’s next on the stack. Once the result is received, JavaScript usually calls back a function you supplied
$.ajax('/myContent.html', function(result) {
$('#myDiv').append(result);
});
In this very simple jQuery example, we are making an ajax call to get the result of /myContent.html file. Once we get a response, we append the received html to the #myDiv DOM element. Quite simple hey ? Now what if we want to make two calls: call1 and call2 and display an error if any of the calls produce an error. We could write this kind of code:
$('#myDiv').append(result);
}, function(error) {
// error
$('myDiv').append('Error');
})
$('/myContent2', function(result) {
$('myDiv').append(result);
}, function(error) {
$('myDiv').append('Error');
})
This works but the problem is that the error would be displayed twice. We could of course check for the error by setting a boolean variable, but still, this wouldn’t be the best way to describe this behavior.
Promises to the rescue
A promise represents the value of an async operation. A promise can have the following three states:
- resolved
- rejected
- pending
Using jQuery’s implementation of the promises - called Deferred -. jQuery already makes use of promises for Ajax calls, so a simple Ajax call could be handled this way:
$.ajax('/content').done(function(result) {
$('#myDiv').append(result);
}).fail(function(error) {
$('#myDiv').append('error');
})
At first glance, this doesn’t look so much better than the first piece of code, right ? Well, the good thing about the promises, is that we may store the promise for later use and even chain them. Now let’s have a look at the previous piece of code. Using the promises we could write it like this:
// make an ajax call to myContent1, if sucessful append the result to #myDiv
var promise1 = $.ajax('/myContent1').done(function() {
$('#myDiv').append(result); }
);
// make an ajax call to myContent2, if sucessful append the result to #myDiv
var promise2 = $.ajax('/myContent2').done(function() {
$('#myDiv').append(result); }
);
Now checking that both ajax calls were successfull would be a matter of checking that both promises have been fullfilled:
$.when(promise1, promise2).done(function() {
// both ajax calls were successful
$.ajax('/content3').done();
}).fail(function() {
$('#myDiv').append('error');
});