I have following nodejs code:
app.get('/dashboard', function(req, res){
db.collection('com_url_mamy').find().toArray(function(err, doc){
db.collection('com_url_mamy').find({'price':''}).count(function(err, docs){
db.collection('com_url_mamy').find({"price":{$not:{$ne:"last_price_1"}}}).count(function(err, last_doc){
if(err){console.log(err);}
console.log(docs);
res.render('dashboard',{'doc':doc, 'docs':docs, 'last_doc':last_doc});
});
});
});
Here i have to add more two or three query/callback.
But I don't think this is right way to do.
Please any one can me tell me how I can solve this problem to increase performance.
Thank You
Using async/await will be an appropriate solution that avoids the callback hell for you in this case. Consider running your queries as follows:
app.get('/user/:name', async (req, res, next) => {
try {
const docs = await db.collection('com_url_mamy').find().toArray()
const count = await db.collection('com_url_mamy').find({'price':''}).count()
const last_doc = await db.collection('com_url_mamy').find({"price": "last_price_1"}).count()
res.render('dashboard', { docs, count, last_doc })
} catch (err) {
return next(err)
}
}
As an alternative, you can use the async libary especially the async.parallel() method when you need to run multiple tasks that do not depend on each other and when they all finish do something else.
Consider the following example:
app.get('/user/:name', function(req, res, next) {
var locals = {};
async.parallel([
// Load all documents
function(callback) {
db.collection('com_url_mamy').find().toArray(function(err, docs){
if (err) return callback(err);
locals.docs = docs;
callback();
});
},
// Get count of documents where price is empty
function(callback) {
db.collection('com_url_mamy').find({'price':''}).count(function(err, count){
if (err) return callback(err);
locals.count = count;
callback();
});
},
// Load last docs
function(callback) {
db.collection('com_url_mamy').find({"price": "last_price_1"}).count(function(err, docs){
if (err) return callback(err);
locals.last_doc = docs;
callback();
});
}
], function(err) { //This function gets called after the three tasks have called their "task callbacks"
if (err) return next(err);
//Here render dashboard with locals object
res.render('dashboard', locals);
});
});
You can use native Promises with MongoDB driver (on node.js >= 0.12):
app.get('/dashboard', function(req, res){
var proms = [];
proms.push(db.collection('com_url_mamy').find().toArray());
proms.push(db.collection('com_url_mamy').find({'price':''}).count());
proms.push(db.collection('com_url_mamy').find({"price": "last_price_1"}).count());
Promise.all(proms)
.then(function(pres) {
res.render('dashboard',{'doc':pres[0], 'docs': pres[1], 'last_doc': pres[2]});
})
.catch(console.error);
});
Promise.all takes the promises you give it and execute them in parallel.
The Promise.all(iterable) method returns a promise that resolves when all of the promises in the iterable argument have resolved
Source:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
(BTW I think you should rename your question with something more like 'Improve independent nested async calls' to avoid the closing/duplicate issue you had.)
Related
In my POST route, im finding two documents from my database, each one with model.findOne. Then I´m trying to take from that one of it´s key/value pair and save it into a variable.
I´ve tried window.______ method, ive tried global._____, but nothing seems to work. I´ve ignored the "var" keyword, but whatever I do, I cant access these variables anywhere else.
app.post("/match", (req, res, next) => {
Team.findOne({name: req.body.team1}, (err, team) => {
if(err) {
console.log(err);
} else {
let eloOne = team.elo; // <-- here is the problem part
}
});
Team.findOne({name: req.body.team2}, (err, team2) => {
if (err) {
console.log(err)
} else {
let eloTwo = team2.elo; // <-- here is the problem part
}
});
console.log(eloOne) // <-- here i want to use the variables
console.log(eloTwo)
}); // please dont kill me for this code, I've started programing recently
Here is the code.
app.post("/match", (req, res, next) => {
Team.findOne({name: req.body.team1}, (err, team) => {
if(err) {
console.log(err);
} else {
let eloOne = team.elo; // <-- here is the problem part
Team.findOne({name: req.body.team2}, (err, team2) => {
if (err) {
console.log(err)
} else {
let eloTwo = team2.elo; // <-- here is the problem part
console.log(eloOne) // <-- here i want to use the variables
console.log(eloTwo)
res.send(' request complete')
}
});
}
});
});
I suggest to use 'async await' or promise atleast.
Use promise.all as it will be doing both the network calls in parallel, and hence increase the performance.
app.post("/match", async (req, res, next) => {
try {
const [team, team2 ] = await Promise.all([Team.findOne({name: req.body.team1}).exec(), Team.findOne({name: req.body.team2}).exec()]),
eloOne = team.elo,
eloTwo = team2.elo;
console.log(eloOne)
console.log(eloTwo)
} catch(error) {
console.log(error);
}
});
I have a site where i need some data from my Mongo data to be shown. My problem, however, is that i need data from two collections. Collections that are completely separate and have nothing to do with each other.
Right now i have this in my routes for my profile-page:
router.get('/profile', function(req, res,next) {
var resultArray = [];
mongo.connect(url, function(err, db) {
var cursor = db.collection('users').find();
cursor.forEach(function(doc, err) {
resultArray.push(doc);
}, function() {
db.close();
res.render('profile/index', {users: resultArray});
});
});
});
And this, of course, works perfectly fine. But how do i get a second db.collection('colors').find(); to be passed along to my template too?
I'm sure it's something trivial, and me just not quite having the full grasp of things, but yeah.. I'm stuck..
Use the async library which is best suited for this scenario. Where you need to run multiple tasks that do not depend on each other and when they all finish do something else, you should use async.parallel() method. The signature is async.parallel(tasks, callback), where tasks is an array of functions.
It will immediately run all the functions in parallel, wait for all of them to call their task callback, and finally when all tasks are complete it will run callback (the final callback).
The following example demonstrates how this could be adapted for your use case:
router.get('/profile', function(req, res, next) {
mongo.connect(url, function(err, db) {
var locals = {};
var tasks = [
// Load users
function(callback) {
db.collection('users').find({}).toArray(function(err, users) {
if (err) return callback(err);
locals.users = users;
callback();
});
},
// Load colors
function(callback) {
db.collection('colors').find({}).toArray(function(err, colors) {
if (err) return callback(err);
locals.colors = colors;
callback();
});
}
];
async.parallel(tasks, function(err) { //This function gets called after the two tasks have called their "task callbacks"
if (err) return next(err); //If an error occurred, let express handle it by calling the `next` function
// Here `locals` will be an object with `users` and `colors` keys
// Example: `locals = {users: [...], colors: [...]}`
db.close();
res.render('profile/index', locals);
});
});
});
Try this code:
router.get('/profile', function(req, res,next) {
var resultArray = {
users : [],
colors : []
};
mongo.connect(url, function(err, db) {
var cursor = db.collection('users').find();
cursor.forEach(function(doc, err) {
resultArray.users.push(doc);
}
var colors = db.collection('colors').find();
colors.forEach(function(doc,err){
resultArray.colors.push(doc);
}, function() {
db.close();
res.render('profile/index', {users: resultArray.users, colors: resultArray.colors});
});
});
});
Didn't have time to check it, but I'm pretty sure that it would work.
I've got following code now:
exports.listByUser = function(req, res) {
Attack.find({user: req.user._id}, function(err, attacks) {
if(err)
return next(err);
for(var i in attacks) {
attacks[i].evaluateFight();
}
res.json(attacks);
});
};
the main problem is that attacks[i].evaluateFight() is called asynchronously, I want to transform it to make sure that [i-1] iteration is done ... and finally call res.json(attacks). I think, it can be done with async, but I don't know how :( Something like this should work, but how can I call attacks.method?
async.eachSeries(attacks, function (callback) {
//something??
callback();
}, function (err) {
if (err) { throw err; }
res.json(attacks);
});
You can leverage async whilst method call to implement the same. However, there is question I have about the callback of evaluateFight because if it is executed asynchronously then there has to be some callback associated with it which will notify if the previous call is succeeded.
The example code can be as follows assuming evaluateFight returns a callback when completed -
exports.listByUser = function(req, res) {
Attack.find({user: req.user._id}, function(err, attacks) {
if(err)
return next(err);
var attacksLength = attacks.length;
var count = 0;
async.whilst(function () {
return count < attacksLength;
},
function (callback) {
attacks[count].evaluateFight(function(err, result){
count++;
callback();
}); // assuming it returns a callback on success
},
function (err) {
// all the iterations have been successfully called
// return the response
res.json(attacks);
});
};
I'm trying to wrap my head around the async library, but I'm pretty wobbly in NodeJs and I can't figure out async.parallel. The code below produces error TypeError: undefined is not a function on the line where the parallel tasks are to be executed. Am I correct in that tasks to be run in async.parallel should have a callback() when they are done? (irrelevant parts of the function are redacted)
function scrapeTorrents(url, callback) {
request(url, function(err, res, body) {
if(err) {
callback(err, null);
return;
}
var $ = cheerio.load(body);
var results = [];
var asyncTasks = [];
$('span.title').each(function(i, element){
// scrape basic info
var show = {title: info.title, year: info.year};
asyncTasks.push(
getOmdbInfo(show, function (err, res) {
if (res) {
omdbInfo = res;
results.push({
// add basic info and Omdb info
});
}
callback();
})
);
});
async.parallel(asyncTasks, function(){
callback(null, results);
});
});
}
In the section where you define async tasks, be sure to specify a closure with a parameter method to call once the task is complete (named differently than callback so as to avoid hoisting).
asyncTasks.push(
function (done) {
getOmdbInfo(show, function (err, res) {
if (err) {
return done(err);
}
if (res) {
omdbInfo = res;
results.push({
// add basic info and Omdb info
});
}
return done();
})
}
);
I have a collection of posts and a collection of users. When returning the list of posts, I want to resolve the references to users. This means making an async call for every row of the users. When monk returns a promise, it returns something that responds to "complete" or "success". Q expects something responding to "then". I need to use Q.all to wait for all the users to be fetched into the posts, but I can't make it play well with monk's promise style.
Here is my attempt.
exports.posts = function (req, res) {
req.posts.find()
.complete(function(err, posts) {
handle(err, res, posts);
var postsWithUsers = posts.map(function(post) {
return req.users.findOne({_id: post.userId}).complete(function(err, result) {
post.user = result;
});
});
Q.all(postsWithUsers.map(function(monkPromise) {
monkPromise.then = monkPromise.complete
}), function(err, results) {
console.log("done with all posts");
});
});
};
Just for everyone else out there. This is one solution, perhaps not the best.
exports.posts = function (req, res) {
req.posts.find()
.complete(function(err, posts) {
handle(err, res, posts);
var postsWithUsers = posts.map(function(post) {
var deferred = Q.defer();
return req.users.findOne({_id: post.userId}).complete(function(err, result) {
post.user = result;
deferred.resolve(result);
});
return deferred.promise;
});
Q.all(postsWithUsers, function(err, results) {
console.log("done with all posts");
});
});