0

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!

Amal Antony
  • 6,477
  • 14
  • 53
  • 76
  • You are right about this being blocking code. For non-blocking code you need to use something like `setTimeout`, rely on built-in asynchronous methods (i.e. HTML5 FileReader API), ajax requests, web workers or DOM events. – Ingo Bürk Sep 29 '13 at 16:29
  • @IngoBürk : Could you write a detailed reply demonstrating what you mean? Maybe as an answer? – Amal Antony Sep 29 '13 at 16:32
  • Done. Seeing the second answer I'm gonna point out that I'm talking about browser-javascript. Node.js has other options (see the other answer). /edit: Just now noticed you tagged your question with nodejs. – Ingo Bürk Sep 29 '13 at 16:50
  • Once you get your head wrapped around callbacks, you may also want to take a look at [promises](http://howtonode.org/promises). Once you start chaining async functions in callbacks you end up with pyramid code or callback soup. – Kerry Liu Sep 29 '13 at 16:58
  • @IngoBürk : The example in the question relates to db.query, I'm believe that itself is a fair indication of it being back end code ;). – Amal Antony Sep 29 '13 at 17:10
  • I guess so, but I just assumed it was an example. :) – Ingo Bürk Sep 29 '13 at 17:17

2 Answers2

3

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".

hexacyanide
  • 88,222
  • 31
  • 159
  • 162
  • Just to clarify this: Your solution is the node.js solution – Ingo Bürk Sep 29 '13 at 16:47
  • I will make note in the post, as I was unsure which the person who asked was asking about. – hexacyanide Sep 29 '13 at 16:49
  • I just now realized that the question is actually tagged with nodejs – my fault. – Ingo Bürk Sep 29 '13 at 16:50
  • @hexacyanide great answer! Very succinct. – Amal Antony Sep 29 '13 at 17:18
  • A good cross-platform alternative to `process.nextTick` is `setTimeout(...,0)` which never executes immediately (even with timeout of zero), is always asynchronous, executes in the next round of the event loop and thus on node.js behaves exactly equivalent to `process.nextTick` – slebetman Sep 27 '14 at 07:40
  • It *might* behave the same on Node.js, but there's probably a reason the documentation states that `process.nextTick()` "is not a simple alias to `setTimeout(fn, 0)`", and that "it's much more efficient". – hexacyanide Sep 27 '14 at 19:01
2

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

Community
  • 1
  • 1
Ingo Bürk
  • 19,263
  • 6
  • 66
  • 100