node - lost in nested callbacks and scopes - node.js

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.

Related

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

nodejs async two arrays and mongoose find

I am trying to use async module on my nodejs application with not lucky.
Suppose i have the following:
/**
* parents=['parent1Template','parent2TEmplate',...]
* children=[[{id: 'child1Parent1'}{id: 'child2Parent1'}],[{id: 'child1Parent2"}{id: 'child2Parent2'}],...]
*
*/
function createTemplate(parents,children){
var final=[]
async.each(Object.keys(parents), function(item,done){
if(children[item].length!==0) addChildsByParent(parents[item],children[item], function (result) {
final.push(result);
});
done();
});
console.log("Final results: "+final);
return final;
}
function addChildsByParent (parent,childs,callback){
var template=[];
//some operations..
async.each(childs,function(child){
Children.findone({"_id": child.id}, function (err,ch)){
template.push(ch);
}
});
return callback(template)
}
I need to get in final all results when all operations have finished.
I have seen also functions as parallel and waterfall on async module but the main problem is that i need to work always with two arrays and do the query when i get the single value.
What's the best way, maybe something like this?
async.waterfall([
each()...
async.waterfall([
each()...
])
])
I would use async map instead of each to build the response array using the map callback.
Also, I would use async parallel instead of waterfall to improve speed, since operations don't depend on each other and can be executed in parallel.
async.parallel({
final : function(next) {
async.map(Object.keys(parents), function(item,done){
if(children[item].length!==0) addChildsByParent(parents[item],children[item], function (result) {
done(null, result);
});
}, next);
},
template : function(next) {
async.map(childs, function(child, done) {
Children.findone({"_id" : child.id}, function (err, ch) {
done(err, ch);
});
});
}
}, function(error, results){
if (!error) {
console.log(results);
// This will be {final:[], template:[]}
}
});

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

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