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

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);
}
});
}
});

Related

Understanding callback on this MDN example

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

How the Callback() really works ?How to insert multiple collections in mongodb using callbacks.?

I am new to node, I am trying to insert multiple collection.I have tried below code seeing some examples and its workng fine
function createUser(admindata, callback) {
admindata.save(callback);
}
exports.create = (req, res) => {
const admindata = new Note({
fname: req.body.fname,
lname: req.body.lname || "Untitled Note",
age: req.body.age,
})
createUser(admindata, function (err, result) {
if (err) throw err;
lastInsertId = result._id;
if (lastInsertId) {
const usersData = new Address({
user_address_id: lastInsertId,
pincode: req.body.pin,
city: req.body.city,
state: req.body.state
})
createUser(usersData, function (err, result) {
if (err) throw err;
res.send(result);
});
}
});
}
What I really want is there any better ways to make this happen using callback functions and what exaclty the callback() function and how to use it and when to use it.?
A callback is a function that is to be executed after another function has finished executing.
In JavaScript functions are objects so you can pass any function as a parameter to other functions that's why callbacks were use extensively in javascipt.
In your code in the create user function you take function as parameter as you can see.
createUser(admindata,callback) here callback is a function as a parameter.
Then inside the function you are passing that function (callback) to save method which also takes a function as a parameter (callback function).
The 2nd call of the createUser function is in callback of save method therefore it will execute after the completion of first createUser.
In the 2nd call instead of declaring a function you are passing an anonymous function as parameter which will be executed after the execution of 2nd call.
Example - Using anonymous function as callback
createUser(data,function(err,data){
if(err)
{
throw err;
}
else {
console.log(data);
}
})
Example using normal function as callback
function exampleFunction(err,data){
if(err)
{
throw err;
}
else {
console.log(data);
}
}
//passing normal function
createUser(data,exampleFunction) ;

node - lost in nested callbacks and scopes

I am lost in nested callbacks:
var getTweetsCloudant = function(key) {
db.find({
selector: {
name: 'john'
}
, fields: ['name', 'text']
, sort: []
, limit: 5
}, function (er, result) {
if (er) {
throw er;
}
return result;
});
}
console.log(getTweetsCloudant('something')); //--> undefined
This is part of my code, I can't give everthing as there are dependencies to an external module that connects to a Cloudantdb (db.find).
My problem is that console.log return undefined and not the result from the db.find callback.
I do understand that I get undefined because the callback (er, result) is asynchronous from the getTweetsCloudant function. I thought to create a function to be called by the callback (er, result) but the whole thing was becoming a small monster.
What's the best way to get 'result' calling getTweetsCloudant.
(My end goal is to export this function as module to be used by other modules.)
Cheers, Giovanni
db.find is an async function. This means that the result is not immediately delivered and the call is non blocking. The results are only available to the callback you passed to db.find. As the call to db.find will not block, the function will immediately return and not wait for the callback to be executed.
You can use a similar call back based approach to solve your problem.
Change getTweetsCloundants to accept a callback.
var getTweetsCloundants = function(key, next){
db.find({
selector: {
name: 'john'
}
, fields: ['name', 'text']
, sort: []
, limit: 5
}, function (er, result) {
if (er) {
next(er);
}
next(null, result);
});
}
You can now use this function as shown below:
getTweetsCloudant('something', function(err, result){
if(err) { console.log(err) } else { console.log(result) }
})
There are other approaches to deal with asynchronous stuff. Promises and async await.

Async.eachOfSeries not looping

I tried aysnc.eachOfSeries but the code does not loop. It stops executing in the first iteration itself and freezes. I guess I made some error in returning the callback.
I also tried putting a callback inside the else block but gives callback already called error.
This async.eachOfSeries is nested inside one more eachOfSeries.
async.eachOfSeries(data, function (value2, val, callback) {
let jsonObj = data[val];
let email = jsonObj.ToEmail;
jsonObj['retailer'] = res[camp].retailer;
jsonObj['summary'] = 'f';
let tempObj = {};
tempObj[id] = jsonObj;
let options = { new: true};
let campId = id;
User.addCampaignResponse(email, campId, tempObj, options, function (err, results) {
if (err) {
throw err;
} else {
console.log("gets printed once");
Campaign.updateResponse(_id, function (err, results2) {
if (err)
throw err;
else {
console.log("Gets printed once");
callback(); //tried this but gives callback already called error
}
}) // console.log(results);
}
})
}, function (err) {
console.log("Doesn't print");
callback();
})
You must use it like this:
//async.eachOfSeries(data, function (value2, val, callback) { => wrong
async.eachOfSeries(data, function (value2, callback) {
//
})
Thought I'd leave this here in case someone else hits this thread.
The problem is that on your optional callback at the end:
}, function (err) {
console.log("Doesn't print");
callback();
});
You're calling the iterator callback and not an outer callback.
In the context of your code you probably have your async inside other function which callback is the one you should call on your async's optional callback at the end. Or just don't use the optional function, but never let it go through your iterator callback after ending the loop.
You should also test for error, something like:
}, function(err){
if(err){
debug('Something went wrong in async: ' +err);
outerCallback(err, somethingIfitsthecase);
}else{
outerCallback(null, somethingIfitsthecase);
}
});
Check caolan's documentation as Parth Acharya recommended.
Thanks
Best regards
Pedro Velez Silva
It is supposed to work that way.
if an error occurs, which is happening in your case. It runs the final callback function that you've provided.
Link :- https://caolan.github.io/async/docs.html#eachOfSeries
A callback which is called when all iteratee functions have finished,
or an error occurs. Invoked with (err).
I had a similar dilemma and I solved it by adding a callback in the async param and calling it at the end of the method as shown below.
async.eachOfSeries(data, async (item, index, callback: any) => {
// method body
callback();
})

Node.js Async only doing one callback

I'm just doing my first bit of Node async stuff, I wanted to do two queries to a DB and then print the results one after the other, my code is as follows:
console.log(req.query);
function logAllThings(err,things){
if (err){
console.log(err);
} else {
console.log(things);
};
};
async.parallel([
function(callback) { //This is the first task, and callback is its callback task
Event.find({}, function(err, events) {
logAllThings(err,events);
});
},
function(callback) { //This is the second task, and callback is its callback task
Organisation.find({}, function(err,organisations) {
logAllThings(err,organisations);
}); //Since we don't do anything interesting in db.save()'s callback, we might as well just pass in the task callback
}
], function(err) { //This is the final callback
console.log('Both should now be printed out');
});
The issue I have is that the second function (the one that returns organisations) prints them fine. However the one that is meant to return events simply does not and returns {} despite the fact I know the query works as I've tested it elsewhere.
Any help would be appreciated
Thanks
You have to call the callback function passed to each of your waterfall functions, otherwise it won't know when it's finished. Try this:
async.parallel([
function(callback) { //This is the first task, and callback is its callback task
Event.find({}, function(err, events) {
if (err) return callback(err);
logAllThings(err,events);
callback();
});
},
function(callback) { //This is the second task, and callback is its callback task
Organisation.find({}, function(err,organisations) {
if (err) return callback(err);
logAllThings(err,organisations);
callback();
}); //Since we don't do anything interesting in db.save()'s callback, we might as well just pass in the task callback
}
], function(err) { //This is the final callback
console.log('Both should now be printed out');
});

Resources