How are node.js callbacks asynchronous?

I have recently started with node.js. It quotes that node.js asynchronous behaviour can be used in three ways events, stream, callback.

Events and stream work on emit,pipe event making it truly async, but how is callback async, as it executes before the return of the function unless process.nextTick() is used.

Events:

event.on('data',function(data){

});

Callback:

function(data,cb){
 // do something with "data"
 cb();
 return;   
}

Let's take a closer look at your function that has a callback:

function(data, cb) {
 // do something with "data"
 cb();
 return;
}

You have 2 big errors here:

1) You have no reason to use return when you have a callback function. Return is used only when you have synchronous actions that send a response back right away (they do not have to wait for a callback to be triggered sometime in the future).

2) You don't execute the callback immediately, it doesn't make any sense. You either don't use a callback and use a return statement (when you don't really have an async function) or you execute the callback sometime in the future, when you get the result.

Better example:

function (data, cb) {
  // you make an Ajax call and 
  // you don't know when the callback will be triggered
  makeAjaxCall(data, cb);
}

You're correct in that callbacks don't necessarily mean asynchronous I/O.

Events aren't necessarily asynchronous either, in the sense that EventEmitter's .emit works immediately, not on the next tick:

var EventEmitter = require('events').EventEmitter;

var emitter = new EventEmitter();

emitter.on('ev', function(x) { 
    console.log("received event ", x);
});

console.log("Hello");
emitter.emit('ev', "X");
console.log("World");

Result:

Hello
received event  X
World 

However, callbacks can be made "async" by:

  • using process.nextTick(). This doesn't make the actual I/O operations inside the callback async. It just defers execution until the next tick. If some operation blocks, the event loop will also block - on the next tick instead of this tick.

    function(data, cb) {
        process.nextTick(function() { doSomethingWith(data); cb(); })
    });
    
  • calling the specified callback function from an event that is known to be triggered by an async I/O operation

    function(data, cb) {
        emitter.startIO();
        emitter.on('someIOEvent', function(e) { 
            doSomethingWith(data,e); 
            cb(); 
        });
    });
    
  • using another callback-based function known to be async. Most functions in node core and node's modules are like this.

    function(data, cb) {
        otherFunction(data, function(moredata) { 
            doMoreStuffWith(moredata, data); cb(); 
        });
    });
    

In this case, the function with the callback is not being run asynchronously, however there are still cases where such functions can be used asynchronously:

function foo(data, callback) {
    // do something with the data
    callback();
}

function bar() {
    // this will be called as a callback from foo()
}

setInterval(function() { foo(data, bar) }, 1000);
console.log('this is displayed immediately');

You can see that foo() is scheduled to run every second, and that happens asynchronously; however the existence of the callback allows you to set up extra behavior from outside the foo() function, e.g. you might set up several cases where foo() is called with different callbacks, resulting in different application logic.