In my nodejs project, there's a function where I have two queries to get data from. First query is dependent on a flag, if flag is true, then I've to run that query and pass its data to second query. Otherwise I've to set some hard-coded data for second query. But it does not wait for first query response and run second query. I've added callbacks to it but nothing worked for me. Here is my code snippet;
getData(req, callback) {
if(!req.flag){
lstData.push('d');// hardcoded data
return Reader.open(filename).then(reader => {
let code = reader.data;
if(!!code ){
return dao.getFirstData(code , (err, results) => {
if (err) return callback(err);
if(results && results.length > 0){
return lstData = results;
}
});
}
});
}
else{
lstData.push('d');// hardcoded data
}
let asyncFuncs = [
(callback) => {
dao.getSecondData(lstData, (err, results) => {
if (err) return callback(err);
return callback(null, { amounts: results });
});
}
];
asyncFuncs.push(callback => {
dao.thirdFunction(id, callback);
});
async.parallel(asyncFuncs, (err, results) => {
if (err) return callback(err);
let data1= results[0].amount;
let data2= results[1];
// some calculations with data1 & data2
return callback(err, finalResult);
});
}
No matter flag is true or false, getSecondData always returns data against d (hard-coded value). I am a newbie to nodejs, so please let me know what am I doing wrong.
SO I updated my code to add promise in it...here is updated code;
getData(req, callback) {
if(!req.flag){
lstData.push('d');// hardcoded data
Reader.open(filename).then(reader => {
let code = reader.data;
if(!!code ){
var prom = new Promise((resolve, reject) => {
dao.getFirstData(code , (err, results) => {
if (err) return callback(err);
if(results && results.length > 0){
let lstData = results;
return resolve(lstData);
}
});
});
prom.then((result) => {
return result;
});
}
});
}
else{
lstData.push('d');// hardcoded data
}
let asyncFuncs = [
(callback) => {
dao.getSecondData(lstData, (err, results) => {
if (err) return callback(err);
return callback(null, { amounts: results });
});
}
];
asyncFuncs.push(callback => {
dao.thirdFunction(id, callback);
});
async.parallel(asyncFuncs, (err, results) => {
if (err) return callback(err);
let data1= results[0].amount;
let data2= results[1];
// some calculations with data1 & data2
return callback(err, finalResult);
});
}
But still same response. It is not waiting for promise result.
There plenty of ways to achieve this but I would prefer http://async.io/ to perform multi function operations.
By using promise you can do,
function f1(argument){
return new Promise((resolve, reject) => {
try {
data = // do your thing
resolve(data)
}
catch(err){
reject(err)
}
})
}
and then use await to achieve this
async function caller(){
await f1()
}
Few other links to help you [here][2]
[2]: https://stackoverflow.com/questions/22442321/callback-function-example/48333938#:~:text=A%20callback%20function%2C%20is%20a,or%20executed)%20inside%20the%20otherFunction.&text=Without%20thinking%20too%20much%2C%20see%20the%20following%20example.&text=Generally%2C%20JavaScript%20allows%20function%20as%20a%20parameter.
You can use async/await for asynchronous calls or you can uses promises. This will wait at the asynchronous calls and once you get the response your code will execute the dependent subsequent calls.
getData(req, callback) {
if(!req.flag){
lstData.push('d');// hardcoded data --> this might be an issue as you are already pushing data.
// these are async calls where you can ask your code to wait
return Reader.open(filename).then(reader => {
let code = reader.data;
if(!!code ){
var promise = dao.getFirstData(code , (err, results) => {
if (err) reject(err);
if(results && results.length > 0){
lstData = results;
resolve(lstData);
}
});
return promise --> you can subscribe this using then
}
});
}
else{
lstData.push('d');// hardcoded data
}
let asyncFuncs = [
(callback) => {
dao.getSecondData(lstData, (err, results) => {
if (err) return callback(err);
return callback(null, { amounts: results });
});
}
];
}
To get hands on this you can check the ways of using async/await
https://javascript.info/async-await
Related
I am using node-async-loop for asyncronous programming
var array = ['item0', 'item1', 'item2'];
asyncLoop(array, function (item, next)
{
do.some.action(item, function (err)
{
if (err)
{
next(err);
return;
}
next();
});
}, function (err)
{
if (err)
{
console.error('Error: ' + err.message);
return;
}
console.log('Finished!');
});
Like this I am using three async loops one under one.
I want to send the response only after the third inner loop ends. How can I do so?.
Here is the link for node-async-loop (https://www.npmjs.com/package/node-async-loop)
here is my code which i writing but whnever i want to response when the last loop completes it say can set header after send to cliend.
also in console log i am getting data every time when data coming from query.
const id = req.params.id;
finalData = [];
tb_user.findOne({ where: { id: id } }).then((userRiverSys, err) => {
if (userRiverSys) {
// console.log(userRiverSys.regionJson)
asyncLoop(userRiverSys.regionJson, function (item, next) {
// console.log("item", item);
tb_riverSystems.findAll(
{
where: { regionId: item.id }
}).then((findriverSys, err) => {
if (err) {
next(err);
return;
}
// console.log("findriverSys", findriverSys);
if (findriverSys) {
asyncLoop(findriverSys, function (item1, next1) {
if (err) {
next(err);
return;
}
// console.log("item1", item1.dataValues);
tb_facilities.findAll(
{
where: { riverSystemId: item1.dataValues.id }
}).then((findFacilities) => {
if (findFacilities) {
// console.log("findFacilities", findFacilities[0].dataValues.name);
asyncLoop(findFacilities, function (item2, next2) {
if (err) {
next(err);
return;
}
tb_userAccess.findAll(
{
where: { facilityId: item2.dataValues.id }
}).then((userAccessFacilities, err) => {
// console.log("userAccessFacilities", userAccessFacilities[0].dataValues);
// var i = 0;
asyncLoop(userAccessFacilities, function (item3, next3) {
finalData.push({
UserId: item3.userid,
facilityId: item3.facilityId,
})
next3();
},
function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
// i++;
// console.log('Finished!!!!');
// if (userAccessFacilities.length === i) {
// console.log("finalData", i);
// // res.json({"status":"true", "message":"update OrgChallenge"})
// }
})
return res.json({"status":"true", "message":"update OrgChallenge"})
// console.log("finalData", finalData);
})
next2();
}, function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
console.log('Finished!!!');
});
}
});
next1();
}, function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
console.log('Finished!!');
});
}
});
next();
}, function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
console.log('Finished!');
});
} else {
console.log("err3", err)
}
})
If you promisify your asynchronous action (so it returns a promise), then you can just use a regular for loop and async/await and there is no need for a 3rd party library to sequence your asynchronous loop. This is modern Javascript:
const { promisify } = require('util');
do.some.actionP = promisify(do.some.action);
async function someFunction() {
const array = ['item0', 'item1', 'item2'];
for (let item of array) {
let result = await do.some.actionP(item);
// do something with result here
}
return someFinalResult;
}
someFunction().then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
FYI, in real code, many (or even most) asynchronous operations now offer promisified versions of their API already so usually you don't even need to do the promisify step any more. For example, pretty much all databases already offer a promise interface that you can just use directly.
const loop = async (arr, results = []) => {
const item = arr.shift()
if (!item) {
console.log("DONE");
return results;
}
// as async function
await new Promise(resolve => {
resolve(results.push(`asynced-${item}`))
})
return loop(arr, results);
}
(async () => {
const result = await loop(["item0", "item1", "item2"])
console.log(result);
})();
I'd be happy if I can help you.
but this script uses a recursive function instead of node-async-loop.
so this might not be suitable for you.
How to get value from return / callback aggregate in Node.js?
function getAmount(vcode,callback){db.get().collection('general_journal').find({coa_code:vcode}).limit(1).toArray(function(err, result) {
if (err) return callback(err);
callback(null, result);
})}
function getlist(callback) {
db.get().collection().find({}).toArray(function(err, result) {
var vData = [];
if (err) return callback(err);
result.forEach(function(row){
vData.push({coa_code:row.coa_code,amount:getAmount(row.coa_code)})
})
callback(null, vData);
})}
getlist();
Please help to solve this problem, because the Amount value is always undefined, thank you
Because getAmount is asynchronous, you'll have to use Promise.all to wait for all rows' requests to resolve before calling getList's callback. Change getAmount to return a Promise rather than using a callback to make things easier:
function getAmount(vcode){
return new Promise((resolve, reject) => {
db.get().collection('general_journal').find({coa_code:vcode}).limit(1).toArray(function(err, result) {
if (err) return reject(err);
resolve(result);
})
});
}
function getlist(callback) {
db.get().collection().find({}).toArray(function(err, result) {
if (err) return callback(err);
// Map each row to a Promise that resolves to an object:
Promise.all(
result.map(({ coa_code }) => getAmount(coa_code).then(amount => ({ amount, coa_code })))
)
.then(vData => {
callback(null, vData);
});
})
}
Insted of using setTimeout, what should I use after foreach complete?
app.post('/grid', function(req, res){
getResults(req.body.idarray, function(callback){
res.send(callback);
});
});
function getResults(userIds, callback) {
var totalresult = [];
userIds.forEach(function (user) {
sequence
.then(function (next) {
db.query('SELECT given FROM books WHERE user_id = ?', [user.userId], function (err2, result) {
if (err2) throw err2;
next(err, result);
});
})
.then(function (next, err, books) {
db.query('SELECT received FROM encycs WHERE user_id = ?', [user.userId], function (err3, result2) {
if (err3) throw err3;
next(err, result2, books);
});
})
.then(function (next, err, books, encycs ) {
Calculation(books, encycs, function (cb) {
totalresult.push(cb);
});
next();
});
});
setTimeout(function() {
console.log(totalresult); // output ok.
return callback(totalresult); // returning as expected
}, 2000);
}
I dont know what totalresult.length is. So i can't check the length.
So, according to your use case you need to call callback somehow and pass totalresult into it, because that what your external code, code in the route expected.
To do that, you can call callback before calling next of the third .then statement. Like that.
...
.then(function (next, err, books, encycs ) {
Calculation(books, encycs, function (cb) {
totalresult.push(cb);
});
callback(totalresult);
next();
//console.log(totalresult); //output OK.
});
This might work.
Update 1
It is hard to follow with your code. Can't catch up the logic of it. I would propose you Promises approach. I prepared that solution, that might work. It might contain little errors, but it represents the main idea of what you are trying to achieve, and how it can be done.
app.post("/grid", (req, res) => {
getResults(req.body.idarray)
.then(data => {
res.status(200).json(data);
})
.catch(err => {
console.error("Error occured", err);
res.status(500).json(err);
});
});
function getResults(userIds) {
let promises = userIds.map(loadCalculation);
//this will wait until all loadings are done
return Promise.all(promises);
}
function loadCalculation(user) {
//parallel loading of the books and encycs
return Promise.all([loadBooks(user), loadEncycs(user)])
.then(results => {
let books = results[0];
let encycs = results[1];
let totalresult = [];
Calculation(books, encycs, function (cb) {
totalresult.push(cb);
});
return totalresult;
});
}
function loadBooks(user) {
return makeQuery('SELECT given FROM books WHERE user_id = ?', user);
}
function loadEncycs(user) {
return makeQuery('SELECT received FROM encycs WHERE user_id = ?', user);
}
function makeQuery(query, user) {
return Promise((resolve, reject) => {
db.query(query, [user.userId], function (err, result) {
if(err) {
reject(err);
} else {
resolve(result);
}
});
});
}
Please, note that this is not really performant way to load the data from database, at least, I'm sure that you there is a possibility to load all the books and encycs with a single query, because you are using SQL, and it is really flexible language.
Hi I have a problem running a loop and getting the return data using Promises.
I have a getStudentMarks method for getting students marks from the database in subject wise.
getStudentMarks: function(studentId, studentStandard) {
console.log("getStudentMarks invoked...");
return new Promise(function(resolve, reject) {
r.table('student_subjects').filter({
"studentId": studentId,
"studentStandard": studentStandard
}).pluck("subjectId", "subjectName").run(connection, function(err, cursor) {
if (err) {
throw err;
reject(err);
} else {
cursor.toArray(function(err, result) {
if (err) {
throw err
} else {
console.log(result.length);
if (result.length > 0) {
studentSubjectArray = result;
var studentMarksSubjectWiseArray = [];
studentSubjectArray.forEach(function(elementPhoto) {
r.table('student_marks').filter({
"studentId": studentId,
"subjectId": studentSubjectArray.subjectId
}).run(connection, function(err, cursor) {
if (err) {
throw err;
reject(err);
} else {
cursor.toArray(function(err, result_marks) {
var studnetMarksDataObject = {
subjectId: studentSubjectArray.subjectId,
subjectName: studentSubjectArray.subjectName,
marks: result.marks
};
studentMarksSubjectWiseArray.push(studnetMarksDataObject);
});
}
});
});
resolve(studentMarksSubjectWiseArray);
}
}
});
}
});
});
}
I'm invoking the method by,
app.post('/getStudentMarks', function(req, reqs) {
ubm.getStudentMarks(req.body.studentId, req.body.studentStandard)
.then((data) => {
console.log('return data: ' + data);
})
.catch((err) => {
console.log(err);
});
});
When I run the code its working absolutely fine there is no error. I get all the student marks object in the studentMarksSubjectWiseArray array. But the problem is even before the studentSubjectArray loops gets completed, the resolve is getting executed and I'm getting a blank array as return. How do I solve the problem. I understand that I'm not doing the Promises right. I'm new to Promises so I'm not being able to figure out the right way.
That happens because inside your studentSubjectArray.forEach statement you perform set of asynchronous operations r.table(...).filter(...).run() and you push their result into the array. However, those actions finish after you perform the resolve(), so the studentMarksSubjectWiseArray is still empty. In this case you would have to use Promise.all() method.
let promisesArray = [];
studentSubjectArray.forEach((elementPhoto) => {
let singlePromise = new Promise((resolve, reject) => {
// here perform asynchronous operation and do the resolve with single result like r.table(...).filter(...).run()
// in the end you would perform resolve(studentMarksDataObject)
r.table('student_marks').filter({
"studentId": studentId,
"subjectId": studentSubjectArray.subjectId
}).run(connection, function(err, cursor) {
if (err) {
throw err;
reject(err);
} else {
cursor.toArray(function(err, result_marks) {
var studnetMarksDataObject = {
subjectId: studentSubjectArray.subjectId,
subjectName: studentSubjectArray.subjectName,
marks: result.marks
};
resolve(studnetMarksDataObject);
});
}
});
});
promisesArray.push(singlePromise)
});
Promise.all(promisesArray).then((result) => {
// here the result would be an array of results from previously performed set of asynchronous operations
});
In a gulp task I have the following code that creates an array of gitAction promises that get executed within a Promise.all() statement. Afterwards, I'm calling a further statement in a then(). But the then() is being called before the git pulls in the all() have terminated. Any clues please?
var git = require('gulp-git');
var gitActionPromise = function(repo, url) {
console.log('git action '+repo);
var pathToRepo = './repos/'+repo;
if (fs.lstatSync(pathToRepo).isDirectory()) {
return new Promise((resolve, reject) => {
git.pull('origin', 'master', {cwd: pathToRepo}, function (err) {
console.log(repo + " pull done!");
if (err) {
console.log('error');
reject(err);
} else {
console.log('ok');
resolve();
}
})
})
} else {
return new Promise((resolve, reject) => {
git.clone(url, {cwd: pathToRepo}, function (err) {
console.log(repo + " clone done!");
if (err) {
console.log('error');
reject(err);
} else {
console.log('ok');
resolve();
}
})
})
}
};
var repos = package.repos || {};
var promises = Object.keys(repos).map(function(repo) {
return gitActionPromise(repo, repos[repo]);
});
Promise.all(promises).then(
console.log('something else') <= this line was causing my issue
); needed to be enclosed in function
You have to pass a function to then:
Promise.all(promises).then(function() {
console.log('something else');
});
The code you have simply logs "something else" right away.