Understanding callback on this MDN example - node.js

The code comes from an MDN tutorial on how to use Node.js and mongoose. The idea is to make parallel request to get the count of documents in different models. I don't understand where the callback passed to each async.parallel comes from, where it is defined and what it does, it seems like a dummy function to me. Could you help me understand it? Here is the code:
var Book = require('../models/book');
var Author = require('../models/author');
var Genre = require('../models/genre');
var BookInstance = require('../models/bookinstance');
var async = require('async');
exports.index = function(req, res) {
async.parallel({
book_count: function(callback) {
Book.countDocuments({}, callback); // Pass an empty object as match condition to find all documents of this collection
},
book_instance_count: function(callback) {
BookInstance.countDocuments({}, callback);
},
book_instance_available_count: function(callback) {
BookInstance.countDocuments({status:'Available'}, callback);
},
author_count: function(callback) {
Author.countDocuments({}, callback);
},
genre_count: function(callback) {
Genre.countDocuments({}, callback);
}
}, function(err, results) {
res.render('index', { title: 'Local Library Home', error: err, data: results });
});
};

the callback is passed by the async package.
Explanation:
As async parallel function takes array or object (in your example) of asynchronous tasks and these async tasks require a callback which will get called when its execution completes or if there is an error. So parallel function provides these callback functions and will call your callback ( provided as a second parameter to parallel function call) when all of them are called or got an error in any of them.
You can check the detailed explanation from here - https://caolan.github.io/async/v3/docs.html#parallel
Update:
Consider parallel as a wrapper function like:
function parallel(tasks, userCallback) {
let tasksDone = [];
function callback(err, data){ // this is the callback function you're asking for
if(err){
userCallback(err); // calling callback in case of error
}else {
tasksDone.push(data);
if(tasks.length === tasksDone.length){
userCallback(null, tasksDone); // calling callback when all the tasks finished
}
}
}
tasks.forEach(task => {
task(callback); // calling each task without waiting for previous one to finish
})
}
Note: This is not a proper implementation of parallel function of async, this is just an example to understand how we can use callback function internally and what is its usecase

Related

NodeJS - Send multiple requests and process all responses in one callback

I'm trying to find a way to send multiple requests (using Express) and process all the response in one function.
Here's my code :
// In router.js
app.get('/api/FIRST_PATH', CALLBACK_FUNCTION_A );
// In CALLBACK_FUNCTION_A file :
module.exports = function (req, response) {
CALLBACK_FUNCTION_TO_SERVICE_A();
CALLBACK_FUNCTION_TO_SERVICE_B();
CALLBACK_FUNCTION_TO_SERVICE_C();
}
My problem is to send the requests CALLBACK_FUNCTION_TO_SERVICE_A, CALLBACK_FUNCTION_TO_SERVICE_B and CALLBACK_FUNCTION_TO_SERVICE_C and then retrieve all the results in another function to process them.
Any help would be greatly appreciated.
You can learn more about the new js standard and use Promise.
// In CALLBACK_FUNCTION_A file :
module.exports = function (req, response) {
var promises = [CALLBACK_FUNCTION_TO_SERVICE_A(),
CALLBACK_FUNCTION_TO_SERVICE_B(),
CALLBACK_FUNCTION_TO_SERVICE_C()];
Promise.all(promises).then( function(results) {
//results is an array
//results[0] contains the result of A, and so on
});
}
Of course CALLBACK_FUNCTION_TO_SERVICE_A() and such need to return Promise objects. You form a function like this:
function asyncFunction(callback) {
//...
callback(result);
}
You can create a Promise like this:
var p = new Promise(asyncFunction);
It'll start running the function, and supports the Promise interface.
So for example, either use request-promise or you can do something like:
function CALLBACK_FUNCTION_TO_SERVICE_A() {
var worker = function(callback) {
app.get('/api/FIRST_PATH', callback);
};
return new Promise(worker);
}
You can read more about Promise and how to also easily handle errors.
You could use async parallel. You can keep all your API calls as async.parallel array or JSON(Example uses Array).
async.parallel(
[
function(done){
reqServcieA(..., funnction(err, response){
if(err) done(err,null);
done(null, response);
}
},
function(done){
reqServcieA(..., funnction(err, response){
if(err) done(err,null);
done(null, response);
}
},
...// You can keep as many request inside the array
], function(err,results){
// Will be called when all requests are returned
//results is an array which will contain all responses as in request arry
//results[0] will have response from requestA and so on
});

Processing a method synchronously in Node.js

I've inherited some Node.js code and I need to add some functionality. However, I'm not sure syntactically how to accomplish my goal due to the asynchronous nature of Node. Currently, I have a function defined like this:
return {
myEntryPoint: function(req, res) {
var results = getResults();
res.send(200, results);
}
};
Several pieces are calling this function already. Inside of it, I'm calling a function called getResults which is defined like this:
var getResults = function() {
var results = [];
async.series([
function(callback) {
// add to results
},
function(callback) {
// add to results
}
]);
return results;
};
My problem is, I need to wait until all of the functions inside of the async.series call are made before I return the results. How do I do this?
You could change it to add a callback. Series has an optional callback to run once all the functions have completed within.
var getResults = function(finished) {
var results = [];
async.series([
function(callback) {
// add to results
},
function(callback) {
// add to results
}
], function() {
//Called when series is finished
finished(results);
});
};
And to get the results,
return {
myEntryPoint: function(req, res) {
getResults(function(results) {
res.send(200, results);
});
}
};
The usual way to handle this is to take a callback that uses the argument in question.
results = getResults(args);
someFunction(results);
Is equivalent to
function getResults(args, callback) {
var results;
//do stuff and populate results.
callback(results);
}
getResults(args, someFunction);
Except you don't have the issue of trying to wait for async stuff to happen, which you can't do, without weird logic.

async and Q promises in nodejs

I'm using the Q library and async library in nodejs.
Here's an example of my code:
async.each(items, cb, function(item) {
saveItem.then(function(doc) {
cb();
});
}, function() {
});
saveItem is a promise. When I run this, I always get cb is undefined, I guess then() doesn't have access. Any ideas how to work around this?
Your issue doesn't lie with promises, it lies with your usage of async.
async.each(items, handler, finalCallback) applies handler to every item of the items array. The handler function is asynchronous, i.e. it is handed a callback, that it must call when it has finished its work. When all handlers are done, the final callback is called.
Here's how you'd fix your current issue:
var handler = function (item, cb) {
saveItem(item)
.then(
function () { // all is well!
cb();
},
function (err) { // something bad happened!
cb(err);
}
);
}
var finalCallback = function (err, results) {
// ...
}
async.each(items, handler, finalCallback);
However, you don't need to use async for this particular piece of code: promises alone fill this job quite nicely, especially with Q.all():
// Create an array of promises
var promises = items.map(saveItem);
// Wait for all promises to be resolved
Q.all(promises)
.then(
function () { // all is well!
cb();
},
function (err) { // something bad happened!
cb(err);
}
)

async.parallel() - last function not being called

For some reason the 'yyyyyyyyy' string is never printed when I use async.parallel() as per below. Why is this? I thought that the last function would be called once the other two have been called.
var async = require('async');
async.parallel([
function() {
console.log('xxxxxxxxxxx');
},
function() {
console.log('ccccccccccc');
}
], function(err, results){
console.log('yyyyyyyyy');
});
Every function passed in the first parameter to async.parallel should take a callback that it calls when its done so async knows that it has completed:
var async = require('async');
async.parallel([
function(callback) {
console.log('xxxxxxxxxxx');
callback();
},
function(callback) {
console.log('ccccccccccc');
callback();
}
], function(err, results){
console.log('yyyyyyyyy');
});
If an error happens in one of the functions, it should call the callback with
callback(err);
so that async knows an error has occurred and it'll immediately call the last function.

Node.js - Using the async lib - async.foreach with object

I am using the node async lib - https://github.com/caolan/async#forEach and would like to iterate through an object and print out its index key. Once complete I would like execute a callback.
Here is what I have so far but the 'iterating done' is never seen:
async.forEach(Object.keys(dataObj), function (err, callback){
console.log('*****');
}, function() {
console.log('iterating done');
});
Why does the final function not get called?
How can I print the object index key?
The final function does not get called because async.forEach requires that you call the callback function for every element.
Use something like this:
async.forEach(Object.keys(dataObj), function (item, callback){
console.log(item); // print the key
// tell async that that particular element of the iterator is done
callback();
}, function(err) {
console.log('iterating done');
});
async.each is very useful and powerful function which is provided by Async Lib .it have 3 fields
1-collection/array
2- iteration
3-callback
the collection is referred to the array or collection of objects and iteration is refer to the each iteration and callback is optional .
if we are giving callback then it will return the response or say result which you want to show you in the frontend
Applies the function iteratee to each item in coll, in parallel. The iteratee is called with an item from the list, and a callback for when it has finished. If the iteratee passes an error to its callback, the main callback (for the each function) is immediately called with the error.
Note, that since this function applies iteratee to each item in parallel, there is no guarantee that the iteratee functions will complete in order.
exapmle-
var updateEventCredit = function ( userId, amount ,callback) {
async.each(userId, function(id, next) {
var incentiveData = new domain.incentive({
user_id:userId,
userName: id.userName,
amount: id.totalJeeneePrice,
description: id.description,
schemeType:id.schemeType
});
incentiveData.save(function (err, result) {
if (err) {
next(err);
} else {
domain.Events.findOneAndUpdate({
user_id: id.ids
}, {
$inc: {
eventsCredit: id.totalJeeneePrice
}
},{new:true}, function (err, result) {
if (err) {
Logger.info("Update status", err)
next(err);
} else {
Logger.info("Update status", result)
sendContributionNotification(id.ids,id.totalJeeneePrice);
next(null,null);
}
});
}
});

Resources