Hello I am having an issue with the following sequence, I need to run multiple queries which build on each other that are in a for loop then once the final result is obtained to implement the result. I am having an issue where my for loop is looping past the query, also I need to stop the code while the findX function is running.
I know this is an async problem but I don't see how I could chain promises, or use the async npm package with needing to loop queries that depend on the result of the previous query. Thanks in advance.
function findX(){
//executes another query
}
function solve(res, connection, info, requestArr, callback){
var parentID = null;
var obj = {};
for (var i = 0; i <= requestArr.length; i++) {
connection.query("SELECT WHERE ", [parentID, requestArr[i]], function(err, results) {
if(results[0]['x']){
var obj = findX(x)
break;
}else{
parentID = results[0]['parentID'];
}
});
}
//Do stuff with obj only after the final result has been set in the for loop
}
You can use Async TimesSeries.
// Pretend this is some complicated async factory
var createUser = function(id, callback) {
callback(null, {
id: 'user' + id
});
};
// generate 5 users
async.times(5, function(n, next) {
createUser(n, function(err, user) {
next(err, user);
});
}, function(err, users) {
// we should now have 5 users
});
So, in your example would be something like this:
var iterations = requestArr.length - 1,
parentID,
obj;
var getResults = function (i, callback) {
connection.query("SELECT WHERE ", [parentID, requestArr[i]], function (err, results) {
if (results[0]['x']) {
obj = findX(x);
callback('done');
}
else {
parentID = results[0]['parentID'];
callback();
}
});
};
async.timesSeries(iterations, function (n, next) {
getResults(n, function (err) {
next(err);
});
}, function (err) {
// use obj
});
Related
I have these 2 functions
Here I get details from sales table
var getLedgerDetails = function (name, id, res) {
var response = [];
var f = '%d %b %Y';
connection.query("SELECT id,voucher_type,DATE_FORMAT(date,?) as date,amount,voucher_number FROM sales WHERE ledger_name=? and company_id=?", [f, name, id], function (err, result) {
if (err) {
console.log(err)
}
else {
if (result.length > 0) {
var r = JSON.stringify(result, null, 2);
var row = JSON.parse(r);
return row[0];
}
else {
}
}
})
};
and second is
here i want to access the getLedgerDetails Function
getDetails=function(name,id,res){
//**returns undefined**
console.log(getLedgerDetails(name,id,res));
}
but it returns me undefined..It makes the function call but returns no value
where i am wrong??
Its because your code is asynchronous, you have to return your data in a callback function which will be called only at the end.
You can try something like this :
var getLedgerDetails=function(name,id,res, callback) {
var response = [];
var f = '%d %b %Y';
connection.query("SELECT id,voucher_type,DATE_FORMAT(date,?) as date,amount,voucher_number FROM sales WHERE ledger_name=? and company_id=?", [f, name, id], function (err, result) {
if (err) {
callback(err, null);
}
else {
if (result.length > 0) {
var r = JSON.stringify(result, null, 2);
var row = JSON.parse(r);
callback(null, row[0]);
}
else {
callback(null, null);
}
}
});
};
And your getDetails function
getDetails=function(name,id,res){
getLedgerDetails(name, id, res, function(err, row) {
if (err) {
console.log(err);
}
else {
console.log(row);
}
});
};
It seems you want your function getLedgerDetails to return data whereas the anonymous function associated with your connection.query function is actually returning your data. Being the asynchronous nature of javascript
In your case, you can you can use Promises.
Well, Promises provide us with a cleaner code and handling errors with promises is very easy. Also, promises are better when comes to handling nested callbacks that is one after the another.
For Promise:
var Promise = require('promise');
var getLedgerDetails=function(name,id,res) {
return new Promise(function (resolve, reject) {
var response=[];
var f='%d %b %Y';
connection.query("SELECT id,voucher_type,DATE_FORMAT(date,?)
as date,amount,voucher_number FROM sales WHERE
ledger_name=? and
company_id=? ,[f,name,id],function(err,result){
if(err){
reject(err);//or any custom error message.
}else {
if(result.length>0){
var r=JSON.stringify(result,null,2);
var row=JSON.parse(r);
resolve(row[0]);
}else{
}
}
});
}
}
Usage:
getLedgerDetails.then(function(success){
console.log(success)// your db data.
}).catch(function(error) {
// error handle
});
This is my code:
var shopId=[1,2,3,4];
async.each(shopId,function (item,callback) {
model.client.query("SELECT categoryId shopId=? ", [shopId], function (err, rows) {
if (rows.length > 0) {
var asyncData = rows;
//categoryId will be in the format [1,2,3,4]
async.each(asyncData,function (item, callback) {
var categoryId = item.categoryId;
async.series([
function (callback) {
model.client.query("select categoryName from category where categoryId = ?", [categoryId], function (err, data) {
callback(null, data);
});
}],
function (err, results) {
item.category = results[0];
callback();
});
},function (err) {
callback(asyncData); //getting data here
});
}
});
},function (err,result) {
res.json(result); //not getting data here
});
I am new to aysnc methods. I am not able to pass the result to final function.
Supposing that there is a shop table, you can join your tables to save a query.
const sql = 'select categoryName from category as c inner join shop as s on c.categoryId = s.categoryId where s.shopId = ?'
then
const shopId = [1, 2, 3, 4];
const finalResult = [];
async.each(shopId, function(item, callback) {
model.client.query(sql, [shopId], function (err, rows) {
// do not forget error handling !
if (rows.length > 0) {
finalResult.push(rows);
return callback();
}
return callback(); // do not forget to return callback in every case
});
}, function(err) { // in async each callback, you only get errors
if (err)
return res.json(err); // or whatever error handling
res.json(finalResult);
});
Also, considering your code, I would advise to name differently your callbacks to avoid errors or forgets. i.e.
async.each([], function(item, cbkGlobal) {
async.each([], function(item, cbkEach) {});
});
And async.series is usefull when you want multiple functions to get execute in order. But here you are executing only one function...
What I am currently trying to do is to display all the playlists with the songs in it. To do that I first find every playlists, then I do a for to loop through them all (in the same time I initialize globalArr and put the values then it will be sended as json because it's an API) and the problem is when I do another find in the loop(PlaylistSong.find or Song.find) well since it's asynchronous the find will be made when the for will be over, and I will have 0 results because they will take the value of increment when he will be at his maximum. I heard of async, I even googled but I really don't understand how to put through this code because it's a combination of for loops and async queries...
Thanks for your help.
router.get('/', function(req, res, next) {
Playlist.find(function (err, playlists) {
if (err) return next(err);
/* Loop through every playlists */
var globalArr = [];
for (var increment = 0; increment < playlists.length; ++increment)
{
globalArr[increment] = [];
globalArr[increment]["name"] = playlists[increment].name;
/* Loop through every links between Songs and Playlist */
PlaylistSong.find({idPlaylist: playlists[increment]._id}, function (err, songs) {
if (err) return next(err);
for (var songIncrement = 0; songIncrement < songs.length; ++songIncrement) {
{
console.log("increment"+increment);
globalArr[increment][songIncrement] = [];
/* Getting the actual song by his ID */
Song.find({_id: song.idSong}, function (err, song) {
if (err) return next(err);
globalArr[increment][songIncrement]["name"] = songs[songIncrement].name;
globalArr[increment][songIncrement]["artist"] = songs[songIncrement].artist;
globalArr[increment][songIncrement]["picture"] = songs[songIncrement].picture;
globalArr[increment][songIncrement]["price"] = songs[songIncrement].price;
globalArr[increment][songIncrement]["file"] = songs[songIncrement].file;
globalArr[increment][songIncrement]["difficulty"] = songs[songIncrement].difficulty;
globalArr[increment][songIncrement]["downloaded"] = songs[songIncrement].downloaded;
});
}
}});
}
res.contentType('application/json');
res.send(JSON.stringify(globalArr));
});
});
See this question and the accepted answer:
Simplest way to wait some asynchronous tasks complete, in Javascript?
It basically says to use the Async module, push all of your async function calls onto it and then use async.parallel() which gives you a callback when all of the async functions have completed.
I haven't tested it, but something like this seems like it might work:
var async = require('async');
var calls = [];
router.get('/', function(req, res, next) {
Playlist.find(function (err, playlists) {
if (err) return next(err);
/* Loop through every playlists */
var globalArr = [];
for (var increment = 0; increment < playlists.length; ++increment)
{
(function() {
var i = increment;
calls.push(function(callback) {
globalArr[i] = [];
globalArr[i]["name"] = playlists[i].name;
/* Loop through every links between Songs and Playlist */
PlaylistSong.find({idPlaylist: playlists[increment]._id}, function (err, songs) {
if (err) return next(err);
for (var songIncrement = 0; songIncrement < songs.length; ++songIncrement) {
{
console.log("increment"+i);
globalArr[i][songIncrement] = [];
/* Getting the actual song by his ID */
Song.find({_id: song.idSong}, function (err, song) {
if (err) return next(err);
globalArr[i][songIncrement]["name"] = songs[songIncrement].name;
globalArr[i][songIncrement]["artist"] = songs[songIncrement].artist;
globalArr[i][songIncrement]["picture"] = songs[songIncrement].picture;
globalArr[i][songIncrement]["price"] = songs[songIncrement].price;
globalArr[i][songIncrement]["file"] = songs[songIncrement].file;
globalArr[i][songIncrement]["difficulty"] = songs[songIncrement].difficulty;
globalArr[i][songIncrement]["downloaded"] = songs[songIncrement].downloaded;
});
}
callback();
}});
});
})();
}
async.parallel(calls, function(err, result) {
if (err) {
// TODO: Handle error here
}
res.contentType('application/json');
res.send(JSON.stringify(globalArr));
});
});
});
or if you don't want then to execute in parallel, you can use async.series() instead.
See this jsFiddle for a simplified example of your situation... https://jsfiddle.net/bpursley/fj22hf6g/
Yeah, you should use async. I'll explain this in more detail later (son must go to bed...)
PlaylistSong.statics.findSongsByPlaylistId = function(id, done) {
PlaylistSong.find({idPlaylist: id}, function(err, songs) {
if (err) {
done(err)
return
}
var getSongsFns = songs.map(function(song) {
return function(callback) {
Song.find({_id: song.idSong}, callback)
}
})
async.parallel(getSongsFns, done)
})
}
router.get('/', function(req, res, next) {
Playlist.find(function (err, playlists) {
if (err) return next(err);
var getSongsFns = playlists.map(function(playlist) {
return function(callback) {
PlaylistSong.findSongsByPlaylistId(playlist._id, callback)
}
})
async.parallel(getSongsFns, function(err, songs) {
if (err) {
res.status(500).send()
return
}
res.contentType('application/json');
res.send(JSON.stringify(songs));
})
});
});
I am writing the filter using mongodb native driver, but it's driving me this error when you run the query.
In the case of this driver, it has no exec?
What is another way to perform this query?
exports.findAll = function(req, res) {
MongoClient.connect(url, function(err, db) {
var section = req.params.section;
var collection = db.collection(section);
var filter = req.query.filter ? {nameToLower: new RegExp('^' + req.query.filter.toLowerCase())} : {};
var query = collection.find(filter);
var count = 0;
collection.count(filter, function (error, result) {
count = result;
});
if(req.query.order) {
query.sort(req.query.order);
}
if(req.query.limit) {
query.limit(req.query.limit);
if(req.query.page) {
query.skip(req.query.limit * --req.query.page);
}
}
query.exec(function (error, results) {
res.json({
count: count,
data: results
});
});
});
};
Error:
TypeError: undefined is not a function
Better to use the async library in this case as it simplifies the code. In the case where you need to run multiple tasks that depend on each other and when they all finish do something else, use the
async.series() module. The following demonstrates how you can go about this in your case:
exports.findAll = function(req, res) {
var locals = {},
section = req.params.section,
filter = !!req.query.filter ? {nameToLower: new RegExp('^' + req.query.filter.toLowerCase())} : {};
async.series([
// Connect to DB
function(callback) {
MongoClient.connect(url, function(err, db) {
if (err) return callback(err);
locals.collection = db.collection(section); //Set the collection here, so the next task can access it
callback();
});
},
// Get count
function(callback) {
locals.collection.count(filter, function (err, result){
if (err) return callback(err);
locals.count = result; //Set the count here
callback();
});
},
// Query collection
function(callback) {
var cursor = locals.collection.find(filter);
if(req.query.order) {
cursor = cursor.sort(req.query.order);
}
if(req.query.limit) {
cursor = cursor.limit(req.query.limit);
if(req.query.page) {
cursor = cursor.skip(req.query.limit * --req.query.page);
}
}
cursor.toArray(function(err, docs) {
if (err) return callback(err);
locals.docs = docs;
callback();
});
}
], function(err) { //This function gets called after the three tasks have called their "task callbacks"
if (err) return next(err);
// Here locals will be populated with 'count' and 'docs'
res.json({
count: locals.count,
data: locals.docs
});
res.render('user-profile', locals);
});
};
Hi I have this code but when finish the result is not the espected because didn't run in the sequence that I wish
here is the code:
var user_data = {};
models.games.find({$or: [{w_id: req.user._id}, {b_id: req.user._id}, {owner: req.user._id}]}, function (err, games) {
var req_games = [];
if (!err) {
for (var i in games) {
req_games.push(games[i]);
models.users.findOne({_id: games[i].w_id}, function (err, user) {
req_games[i].w_id = user.user;
console.log(req_games[i].w_id) //< -- 3
});
console.log('a ' + req_games[i].w_id) //<-- 2
}
user_data.games = req_games; // <-- 1
}
});
at the end of the task req_games didnt have any update because it's running in the sequence that I put in the comments in the code
This may help you using Q(promises)
obj.find = function(model, condition) { //make your find to return promise
var deferred = q.defer();
model.find(condition, function(err, results) {
if (err) {
logger.log(err);
deferred.reject(err);
} else {
deferred.resolve(results);
}
});
return deferred.promise;
}
ArraysOfId.forEach(function (id) {
var tempProm = mongoUtilsMethodObj.find(schemaObj.Asset, id).then(function (assetObj) {
---- your code
return q.resolve();
});
promArr.push(tempProm);//push all promise to array
});
q.all(promArr).then(function () {
// this will be called when all promise in array will we resolved
})
Here is a version using the async library to map your game values.
var async = require('async');
var user_data = {};
models.games.find({$or: [{w_id: req.user._id}, {b_id: req.user._id}, {owner: req.user._id}]}, function (err, games) {
if(err) {
// or whatever your error response happens to be
return res.render('user.swig', {error: err});
}
async.map(games, function(game, nextGame) {
models.users.findOne({_id: game.w_id}, function (err, user) {
game.w_id = user.user;
nextGame(err, game);
});
}, function(err, req_games) {
user_data.games = req_games;
res.render('user.swig', {user: user_data});
});
});