Program behaving abnormally due to weird async nature of nodejs - node.js

I get a post from mongodb then using that post's id I get the likes on that post, my postlikes object contains a "post" and the "likedby" array that contains usernames of people who liked it. The problem is that when i get a post, the callback for getting likes on it is somehow complex thats why node runs ahead and I couldn't get the likes i always get likes of the last post in the db mostly because the loop has reached its end then my callback is called for likes. Any solution for this??
for(var i=0;i<posts.length;i++)
{
db.collection('likes',function(err,likesCollec){
console.log("before likes posts[i].post",posts[i].post);
function wait(done){
while(done);
}
done=true;
likesCollec.find({postid:(posts[i]._id).toString()})
.toArray(function(err,likes){
console.log("posts[i].post:",posts[i].post);
postlikes[i].post=posts[i].post;
console.log("postlikes[i].post: ",postlikes[i].post);
for(j=0;j<likes.length;j++)
{
postlikes[i].likedby[j]=likes[j].username;
}
console.log(postlikes[i]);
done=false;
wait(done);
});
});
if(i==(posts.length)-1)
{
return res.json(posts);
}
}
The wait function isn't working properly also maybe I am going to the wrong direction, please help.

You may use bluebird;
var Promise = require('bluebird');
Var p = Promise.resolve();
forEach(posts, function(item, index, arr) {
p.then(new Promise(function(resolve, reject) {
// do here in sync.
resolve(); //in last
}));
});
p.then(function(){
// all tasks launched in the loop are finished.
// do extra here.
});

Use async forEach
https://www.npmjs.com/package/async
async = require('async');
async.forEach(posts,function(post, callback){
// Do functionality here
return callback();
})

Related

How to use multi query with nodejs express mongodb

How to use multi query like find , update , insert , delete in one service on mongodb
I can query this below
router.get('/userlist', function(req, res) {
  User.find({},function (err, docs) {
    res.json(docs);
  });
});
but i want to query like this is an error
router.get('/userlist', function(req, res) {
  var data = array();
  User.find({},function (err, docs) {
     data['data1'] = docs
  })
  Content.find({},function (err, docs) {
    data['data2']  = docs
  })
  res.json(docs)
});
Can anyone help me or It is possible to use query like this?
Thank you
You can use async await to run multiple mongo queries inside a function like below:
router.get('/userlist', async function(req, res) {
var data = array();
try{
//first query
let user_data = await User.find({}).exec();
data['data1'] = user_data;
//second query
let content_data = await Content.find({}).exec();
data['data2'] = content_data;
return res.status(200).json(data)
}
catch(err){
return res.status(400).json({err})
}
});
My question is what is stopping you? Yes, you can do this. I did those kinds of works.
There remain two points.
If you need to perform multiple queries, based on the response of the previous query, you can use async/await or promises. If you don't wanna try those, there is still a way. You can check the response of the previous query, make conditions, and if right, execute the seconde one. And thus you can make a chain of queries.
And if you don't need to rely on the responses, do whatever you want. There is nothing wrong...
Happy Coding!
You have to wait for your database call response. So, You can use promise for this. MDN: Promise Make promise of database call and resolve that after you got data from database. Promise.all will wait until your both the promises resolved. MDN: Promise.all()
router.get('/userlist', function(req, res) {
var data = [];
let promise1 = new Promise((resolve, reject) => {
User.find({},function (err, docs) {
data['data1'] = docs;
resolve();
})
});
let promise2 = new Promise((resolve, reject) => {
Content.find({},function (err, docs) {
data['data2'] = docs
})
});
Promise.all([promise1, promise2]).then(result => res.json(docs))
});

ExpressJS: Why does this output [] on the first GET, and then the next GET returns the data from the previous?

I am experimenting with Express and MongoDB, and have a functional API server that can add, update, delete, and retrieve a single post. The issue I have run into is returning all of the documents from Mongo.
I have a GET route that outputs the results, except it does not behave as I imagined it would. When you run this, the first GET request to /notes returns and empty array, i.e. []
let notes =[];
app.get('/notes', (req, res) => {
async function getNotes() {
try {
await db.collection('notes').find().forEach(function (myDoc) {
notes.push(myDoc);
})
} catch(err) {
console.log(err)
}
console.log(notes);
res.send((notes));
}
getNotes();
});
On the second GET to /notes, however, the data that was pushed into notes[] is returned, and it is then overwritten by the newly pushed data.
Can anyone help me fill in the blank spot in my understanding of this? I imagine there is something that I just didn't understand along the way.
Edit***
I have experimented with this a bit, and am still running into the same issues.
let array= [];
async function getNotes() {
try {
await db.collection('notes').find().toArray(function (err, notesArray) {
array = notesArray;
})
} catch (err) {
console.log(err)
}
console.log(array);
return array;
}
app.get('/notes', (req, res) => {
getNotes();
res.send(array);
});
MongoDB's .toArray() won't both invoke a callback and return a Promise.
Returns:
Promise if no callback passed
And, await depends on the Promise, so you won't be able to use the callback as well.
Though, one isn't really necessary. When the promise resolves, await will return the array of documents, allowing you to assign that to array or any other variable.
try {
array = await db.collection('notes').find().toArray();
}

NodeJS Express Async issue

I have this function which gets some data from my database but i'm having a trouble calling the function and getting the proper response
function getEvents()
{
var x = [];
var l = dbCollection['e'].find({}).forEach(function(y) {
x.push(y);
});
return x;
});
and another function which calls this function but it always returns undefined.
How can i make the function wait till mongoose has finished filling up the array?
Thanks for the help! My life
dbCollection['e'].find is called non-blocking way so you are returning x before filling. You need to use callbacks or some mongoose promises. You can get all returning values from database like following snippet
function getEvents(callback) {
dbCollection['e'].find({}, function(error, results) {
// results is array.
// if you need to filter results you can do it here
return callback(error, results);
})
}
Whenever you need to call getEvents function you need to pass a callback to it.
getEvents(function(error, results) {
console.log(results); // you have results here
})
You should read mongoose docs for how queries work.
There is also support for promises in mongoose. You can check this url for more information about promises.
The solution proposed by #orhankutlu should work fine.
I will give another solution using promise. You can choose one between these two solutions depending on your style of programming.
Solution using promise:
function getEvents() {
return new Promise(function(resolve, reject){
dbCollection['e'].find({}, function(error, results) {
if (error) return reject(error);
var x = [];
results.forEach(function(y){
x.push(y);
});
// forEach() is a blocking call,
// so the promise will be resolved only
// after the forEach completes
return resolve(x);
});
});
};
Calling getEvents():
getEvents().then(function(result){
console.log(result); //should print 'x'
}).catch(function(err){
// Handle error here in case the promise is rejected
});
I will encourage you to try both the approaches, ie, using callbacks and using promises. Hope you find it useful!

How to handle callbacks in Node.js?

Let's say I have 3 files.
index.js makes a call to the backend like this
$.post('/test/', data, function(response) {
//handle success here
})
routes.js handles the route like this
app.post('/test/', function(req, res){
item.getItems(function(response){
res.json(response);
});
});
items.js is the model which accesses the database and makes a POST request for each item
function getItems(callback) {
database.query('SELECT * from items', function(result){
result.forEach(function(item){
request.post('/api/', item, function(req, res) {
//finished posting item
});
});
});
//callback here doesnt wait for calls to finish
}
where/when should I call the callback passed to getItems() to handle a success/failure in index.js?
Because your request.post() operations are async, you have to use some method of keeping track of when they are all done and then you can call your callback. There are multiple ways of doing that. I'll outline a few:
Manually Keeping Track of Count of Request Operations
function getItems(callback) {
database.query('SELECT * from items', function(result){
var remaining = result.length;
result.forEach(function(item){
request.post('/api/', item, function(err, res) {
--remaining;
//finished posting item
if (remaining === 0) {
callback();
}
});
});
});
}
The main problem with doing this manually is that propagating error in nested async operations is difficult when you get to actually figuring out how you're going to handle errors. This is much easier in the other methods shown here.
Using Promises
// load Bluebird promise library
var Promise = require('bluebird');
// promisify async operations
Promise.promisifyAll(request);
function queryAsync(query) {
return new Promise(function(resolve, reject) {
// this needs proper error handling from the database query
database.query('SELECT * from items', function(result){
resolve(result);
});
});
}
function getItems(callback) {
return queryAsync('SELECT * from items').then(function(result) {
return Promise.map(result, function(item) {
return request.postAsync('/api/', item);
});
});
}
getItems.then(function(results) {
// success here
}, function(err) {
// error here
})
It seems strange that you're making an API request in your server-side code, unless this is some sort of middle tier code that interacts with the API... but you're interacting with a database, so I'm still confused on why you can't just do a database insert, or have a bulk insert API call?
Anyway, if you must do it the way you're asking, I've done this in the past with a recursive method that trims down the result array... I really don't know if this is a good approach, so I'd like to hear any feedback. Something like this:
function recursiveResult(result, successfulResults, callback) {
var item = result.shift();
// if item is undefined, then we've hit the end of the array, so we'll call the original callback
if (item !== undefined) {
console.log(item, result);
// do the POST in here, and in its callback, call recursiveResult(result, successfulResults, callback);
successfulResults.push(item);
return recursiveResult(result, successfulResults, callback);
}
// make sure callback is defined, otherwise, server will crash
else if (callback) {
return callback(successfulResults);
}
else {
// log error... callback undefined
}
}
function getItems(callback) {
var successfulResults = [];
var result = [1, 2, 3, 4];
recursiveResult(result, successfulResults, callback);
}
console.log('starting');
getItems(function(finalResult) {
console.log('done', finalResult);
});

Nodejs mixed async functions and promises

So I have sails app with some help service to make it easier to create and get complex models.
one of theses are
getMerits: function(profileId, limit){
return async.waterfall([
function(callback){
Merit.find({employeeProfile: profileId}).then(function(merits){
callback(null, merits);
});
},
function(merits, callback){
async.forEach(merits, function(item, loop_callback){
MeritIndex.findOne({id: item.index}).then(function(meritIndex){
merits[merits.indexOf(item)].index = meritIndex;
loop_callback();
});
}, function(err, results){
callback(null, merits);
});
}
], function(err, results){
return results;
});
}
the trouble is when I try to call this function to get the result(list of merits with their meritindexes inserted.) I cant figure out the correct way to get the results returned from the async waterfall:
async.forEach(profiles, function(item, loop_callback){
MeritService.getMerits(item.id, 5).exec(function(err, merits){
console.log(merits)
profiles[profiles.indexOf(item)].merits = merits;
loop_callback();
});
// MeritService.getMerits(item.id, 5).exec(function(m){
// console.log(m)
// profiles[profiles.indexOf(item)].merits = m;
// loop_callback();
// });
}, function(err){
console.log("PROFILES" + JSON.stringify(profiles))
});
the print of merits here results in undefined. Is there any way to treat async waterfall as a promise and use then instead of exec?
You don't need to use async.waterfall since you already have promises, promises chain already - so adding another library for that logic is redundant. Waterline uses bluebird promises which come with convenience methods already.
Your getMerits can be written as:
getMerits: function(profileId, limit){
var merits = Merit.find({employeeProfile: profileId});
var items = merits.map(function(item) {
return MeritIndex.findOne({id: item.index }).then(function(meritIndex) {
item.index = meritIndex;
});
});
return items.return(merits); // wait for items to be done, and return the merits
}
P.S.
If you use Node 4+ let me know since it gets even simpler.

Resources