Understanding asynchronous JavaScript – Callbacks

Recall Jquery click() function.

$("#btn").click(function() {
alert("Hello World");
});

view raw
jqueryHello.js
hosted with ❤ by GitHub

Have you ever wondered, why would you want to write the block of the code that needs to be executed after the clicking on the button inside another function?

Welcome to the world of asynchronous programming, callbacks!

Callback is a programming convention rather than a special feature of a language. They are useful when you want your program not to be blocked by a time consuming operation. In this case, finding the exact html element by the id might consume time. Once it is found, the function we pass to the click() function is called back.

See the following code.

find(function() {
alert("Found the element");
});
alert("Meanwhile I show up");
function find(callback) {
//The time consuming operation
setTimeout(function() {
//calling the passed callback function after 2 seconds
callback();
} ,2000);
}

view raw
file.js
hosted with ❤ by GitHub

Here, the find function takes 2 seconds to finish its operation.  It accepts a function as its parameter to be executed once the time consuming operation is done. (Don’t worry about the setTimeout() function. Yes, this too is a calling back function, but for the time being lets assume, all what it does is calling the passed function after 2 seconds). “callback” is just a variable that is used to pass the callback function back and forth. You can change it to whatever the name you want to call it. Even though it’s a time consuming operation, the alert in the main routine “meanwhile I show up” is displayed, regardless the synchronization.

Find function could be something that was defined in the API or somewhere in the program that performs IO operations or network calls. It promises to the main program routine that “I will call you back, once my time consuming operation is done”.

Callbacks with parameters

JS is too awesome to be around that we don’t explicitly need to mention what is being passed to the find function is another function. Be it another function with or without arguments and variables, it treats them the way they like to be treated. We can pass any function alongside any type of variable and a function signature. Inside the caller function (find() function) all we have to do is passing the arguments to the callback properly.

find([1,2], function(results) {
alert("Found other elements "+results);
});
alert("Meanwhile I show up");
function find(elements, callback) {
//The time consuming operation
setTimeout(function() {
elements.push(3,4,5);
callback(elements);
} ,2000);
}

view raw
callback1.js
hosted with ❤ by GitHub

Here, we pass an array of 2 elements to find() function. It takes 2 seconds to find other elements and push them to the elements array. Then the resulting array is passed to the callback function as another parameter and from the major program routine it is caught and displayed.

Rolling in the deep

Here we pass the callback function by reference to the 2nd caller function, process().

firstFunctionCall : find([1,2], function(results) {
process(results, onMethodsDone);
});
callBackFunctionForProcess : function onMethodsDone(results1) {
alert("Results : "+results1);
}
alert("meanwhile I show up");
function process(elements, callback1) {
//The time consuming operation
setTimeout(function() {
elements.push(6,7,8);
callback1(elements);
}, 2000)
}
function find(elements, callback) {
//The time consuming operation
setTimeout(function() {
elements.push(3,4,5);
callback(elements);
} ,2000);
}

view raw
callback2.js
hosted with ❤ by GitHub

Once the results are returned to the firstFunctionCall it sends the results to the process() function to add more elements to the array. The callback function for process() is passed by the reference onMethodsDone.

How to make sure passed function is actually a function?

Once your genius code is behind an API you need to throw proper messages to the user that he has not quit got the idea of callbacks or he has mixed up the function signature. You can use Js typeof operator to make sure the retrieved parameter is a reference to an actual function within the caller function. Try the following code passing a simple integer instead of a function at the firstFunctionCall.

firstFunctionCall : find([1,2], function(elements) {
alert("Results : "+elements);
});
alert("meanwhile I show up");
function find(elements, callback) {
//The time consuming operation
setTimeout(function() {
elements.push(3,4,5);
if(typeof callback === "function") {
callback(elements);
}
else {
alert("Retrieved function is not a callback");
}
} ,2000);
}

view raw
callback3.js
hosted with ❤ by GitHub

Dealing with states

Your caller function behind the API may be operating a network call and might have gotten a null result. For the sake of modularity you need to treat the failure in a different way so then the user can handle the failure situation inside another function separately. Try following code by passing null instead of [1,2] to the find() function.

find([1,2], response);
function response(err, result) {
if(err) {
alert(err);
}
else {
alert("operation successful : "+result);
}
}
alert("meanwhile I show up");
function find(elements, callback) {
//The time consuming operation
setTimeout(function() {
if(elements) {
elements.push(3,4,5);
callback(null, elements);
}
else {
callback("Elements array should not be null", null);
}
} ,2000);
}

view raw
callback4.js
hosted with ❤ by GitHub

Will call you back! 🙂

Cheers!

[js-disqus]