Mechanics behind Javascript callbacks being asynchronous

Problem

I'm having a bit of trouble understanding the mechanics of JS callbacks. I have a fair idea of how callbacks can be used in JS, but I do not understand how a callback is asynchronous.

For e.g., if my understanding is correct, a callback is of the nature:

db.query(param1, param2 , callback_fn1(){..} );

And the implementation of db.query() is along the lines of :

 db.prototype.query = function(p1 , p2 , callback ){
      //some code 
      callback();
}

How does the above implementation make db.query an asynchronous function? Does this not mean that a function called callback is passed to query and that function is called inside query? It looks like query is just another synchronous function. Could someone help me understand what I'm overlooking here? Thanks!

Problem courtesy of: Amal Antony

Solution

The code sample you've shown is actually still synchronous because is instructed to run immediately. An asynchronous callback is a callback that doesn't immediately need to be executed, so it doesn't block the event loop until you instruct it to run.

The most common way in Node.js to do this is with process.nextTick() which runs a specified function when the event loop call stack is empty. Here's an example:

var async = function(args, callback) {
  // do some work
  process.nextTick(function() {
    callback(val);
  });
};

Then we call the function like this:

async(args, function(val) {
  console.log(val);
});
console.log('end');

In this example, the function async() and console.log('end') are added to the call stack. The call stack empties once both of those functions are run, and once it's empty, console.log(val) then runs.

If you're still confused, think of process.nextTick() as an optimized version of this code:

var fn = function() {};
setTimeout(fn, 0);

It basically means "run this function as soon as possible when you are not busy".

Solution courtesy of: hexacyanide

Discussion

Edit: I just now realized the question is tagged with node.js. My answer is more about Javascript in the browser, @hexacyanide's answer is more about node.js. I guess knowing both doesn't hurt, though!

The way you posted it the code will indeed be blocking. For asynchronous behavior there are a few things you can utilize, such as

  • setTimeout and setInterval
  • Built-in, asynchronous methods such as from the FileReader API
  • Ajax requests
  • Web workers (see @html5rocks)

Your example code could be written as follows (fiddle):

function doStuff(callback) {
    setTimeout(function () {
        for (var i = 0; i < 1000; i++) {
            // do some busy work
            var x = Math.sqrt(i);
        }

        callback();
    }, 0);
}

console.log('start');
doStuff(function () {
    console.log('callback called');
});
console.log('after doStuff()');

The setTimeout call will allow the Javascript interpreter/compiler (however exactly they work these days) to run the function in a non-blocking matter which is why (most likely), you will see the output

start
after doStuff()
callback called

Note that asynchronousity is different from multi-threading. Javascript is still single-threaded (with the exception of web workers!).

A more in-depth explanation can be found for example here

Discussion courtesy of: Ingo Bürk

This recipe can be found in it's original form on Stack Over Flow.