Node.js callback basics:
Most of the functions (Node and its Libraries (called modules)), are of asynchronous (async) nature.
These functions have a common signature with callback as the last argument: function(arguments.....callback)
.
The callback
is just another JavaScript function. ( yes, in Javascript, functions can be passed around as arguments to other functions). Node.js typical callbacks have a signature with first argument as error (if one happened): callback(err,outputs......)
.
example: first argument is a string, second an object (defined inline) and the last is a function (defined inline).
doSomeWork('running',{doFast:true,repeat:20}, function(err,result){
if(err){
console.log('ohnoes!);
} else {
console.log('all done : %s',result);
}
});
which is equivalent to:
var type = 'running';
var options = {doFast:true,repeat:20};
var callback = function(err,result){
if(err){
console.log('ohnoes!);
} else {
console.log('all done : %s',result);
}
};
doSomeWork(type,options,callback);
So the basic contract here is give a function its arguments and pass a callback to be invoked, when it is done. The passed call back will be invoked some where in future when there is something to return, error or the results.
Multiple nested callbacks are generally less readable and complex:
function uploadAll(path,callback){
listFiles(path,function(err,files){
if(err){
callback(err);
}else{
var uploaded = [];
var error;
for(var i = 0 ; i < files.length; i++){
uploadFile(files[i],function(err,url){
if(err){
error = err;
break;
}else{
uploaded.push(url);
}
});
}
callback(error,uploaded);
}
});
};
But fortunately there are modules like async
that help organize callbacks:
function uploadAll(path,callback){
async.waterfall(
[
function(cb){
listFiles(path,cb);
},
function(files,cb){
async.map(files,uploadFile,cb);
}
],callback);
}
Furthermore, there is a Promises
pattern as well. Future versions support generators which provide many new async patterns.