My NodeJs code is returning undefined value - javascript

This is the code that i have written,
function getAllTasksToThatProjectType(typeId){
let storeTaskIds = [];
let projectTypeTasks = [];
let returnVar = new Array();
ProjectType.findOne({ _id: typeId })
.exec((error, projectType) => {
// if(error) return res.status(400).json({ error })
if(projectType){
projectType.tasks.map(tsk => storeTaskIds.push(tsk.taskId))
ProjectTask.find({ })
.exec((error, projectTask) => {
// if(error) return res.status(400).json({ error })
if(projectTask){
storeTaskIds.forEach(function(tID){
projectTask.forEach(function(tsk){
if(JSON.stringify(tsk._id) == JSON.stringify(tID) || JSON.stringify(tsk.parentId) == JSON.stringify(tID))
projectTypeTasks.push(tsk)
})
})
// Here would be the api return statement.
return projectTypeTasks;
}
})
}
})
}
const taskForPType = getAllTasksToThatProjectType(req.body.typeOfProject)
If i call the function like this, it will set the value of taskForPType to undefined.
I have to do this without a callback function. Any suggestions on how i could i achieve it.

It is returning undefined, because your function hasn't finished when you called. You could rewrite the function asynchronously like this:
async function getAllTasksToThatProjectType(typeId) {
const storeTaskIds = [];
const projectTypeTasks = [];
const returnVar = new Array();
return new Promise(function (resolv, reject) {
ProjectType.findOne({ _id: typeId }).exec((error, projectType) => {
if (error) {
reject(error);
}
if (projectType) {
projectType.tasks.map((tsk) => storeTaskIds.push(tsk.taskId));
ProjectTask.find({}).exec((error, projectTask) => {
if(error) {
reject(error)
}
if (projectTask) {
storeTaskIds.forEach(function (tID) {
projectTask.forEach(function (tsk) {
if (JSON.stringify(tsk._id) == JSON.stringify(tID) || JSON.stringify(tsk.parentId) == JSON.stringify(tID))
projectTypeTasks.push(tsk);
});
});
// Here would be the api return statement.
return resolve(projectTypeTasks);
}
});
}
});
});
}
const taskForPType = await getAllTasksToThatProjectType(req.body.typeOfProject);
For more info you could check more info about async calls:
How do I return the response from an asynchronous call?

Related

My promise is being resolved before my second query is able to run inside of a .map function

const getAdditionalInfosPromise = (ProgramID, LeadId) => {
return new Promise((resolve, reject) => {
connection.query(`SELECT * FROM crm_additionalLeadInfoFields WHERE ProgramID= ${ProgramID};`, (error, response) => {
if (error) reject(console.error(error, 'SQL ERROR IN GETADDITIONALINFOPROMISE'));
else {
let getAdditionalInfosData = [];
const getAdditionalInfos = response;
getAdditionalInfos.map((data, i) => {
​
let tableNameData;
// checking to see what our table name is
if (data.TableName === 'leads') {
tableNameData = `WHERE id= ${LeadId};`
}
else {
tableNameData = `Where LeadId = ${LeadId};`
}
​
// Remove any white spaces
​
const columnName = data.ColumnName.replace(/ +/g, "")
​
connection.query(`SELECT ${columnName} as pertinentValue
FROM ${data.TableName}
${tableNameData}`, (err, res) => {
if (err) console.error(err, 'MY SQL ERR IN GETADDITIONALINFOSPROMISE');
else {
console.log(data);
if (data.DisplayName !== 'Dealer Name') {
const pertinentValue = res[0].pertinentValue
​
getAdditionalInfosData.push({
'id': `additionalItem${i}`,
'label': data.DisplayName,
'value': `${pertinentValue !== null ? pertinentValue : ''}`,
'class': ''
})
​
}
}
})
})
resolve(getAdditionalInfosData)
}
})
​
})
}
Any Idea how to make this asynchronous? I tried using the async npm package but was having issue with getting any type of response back from the async.map(array, function (result,callback) {// Was null here} ). As it is right now it is returning an empty object and then logging my data afterwards Thanks to all who help! :)
Have you tried converting the items in the array you're mapping into promises? Then, using something like Promise.all (see below)? I also went ahead and moved a few items around to make it easier for me to read.
const getAdditionalInfosPromise = (ProgramID, LeadId) => {
return new Promise((resolve, reject) => {
connection.query(`SELECT * FROM crm_additionalLeadInfoFields WHERE ProgramID= ${ProgramID};`, (error, response) => {
if (error) reject(console.error(error, 'SQL ERROR IN GETADDITIONALINFOPROMISE'));
let getAdditionalInfosData = [];
const getAdditionalInfos = response;
const allPromises = getAdditionalInfos.map((data, i) => {
if (data.DisplayName !== 'Dealer Name') {
const tableNameData = data.TableName === 'leads' ? `WHERE id= ${LeadId};` : `Where LeadId = ${LeadId};`;
const columnName = data.ColumnName.replace(/ +/g, "")​
return new Promise((resolve, reject) => {
connection.query(`SELECT ${columnName} as pertinentValue FROM ${data.TableName} ${tableNameData}`, (err, res) => {
if (err) console.error(err, 'MY SQL ERR IN GETADDITIONALINFOSPROMISE');
console.log('within the select query', data);
const pertinentValue = res[0].pertinentValue​
resolve({
'id': `additionalItem${i}`,
'label': data.DisplayName,
'value': `${pertinentValue !== null ? pertinentValue : ''}`,
'class': ''
})​
})
})
}
reject({});
});
console.log(allPromises)
resolve(getAdditionalInfosData)
})​
})
}
Then you can do something like:
Promise.all(allPromises).then(function(values) {
console.log(values);
// do anything you need with your data here
});

Node js pause while loop wait until functions inside get executed completely?

I am coding a post request which downloads all URL HTML,zips them and email it back. This all should happen in the backend. I am storing all the data in an array and extract the first element to start these operations.
I have while loop inside which I am calling some functions. Each function gets executed at a certain time.
I used async, await and promises to make sure they run one after the
other.
Coming to my problem.
My while loop starts getting executed again before all the
functions inside it are executed.
app.post('/?', async (req, res) => {
var urls = req.query.urls
var email = req.query.email;
var new_stack = [urls, email]
stack.push(new_stack)
res.send("Mail sent")
if (isFunctionRunning === false) { //initially it is false
console.log(isFunctionRunning, stack.length)
send_mails();
}
});
const getGoogleIndexHTML = (url) => {
return new Promise((resolve, reject) => {
request(url, (err, res, body) => err ? reject(err) : resolve(body))
})
}
const some_function_to_download = async (url) => {
try {
const a = url.split(".")
let googleIndexHTML = await getGoogleIndexHTML(url)
await fs.writeFile(directory + '/' + a[1] + '.html', googleIndexHTML, (err) => {
if (err) throw err
})
console.log('File created.')
} catch (err) {
console.log(err)
}
}
const html_to_zip_file = async () => {
await zipper.zip(directory, function (error, zipped) {
if (!error) {
zipped.compress();
zipped.save('./package.zip', function (error) {
if (!error) {
console.log("Saved successfully !");
}
});
} else {
console.log(error)
}
})
}
const send_mails = async () => {
while (stack.length > 0) {
isFunctionRunning = true
var a = stack.shift()
var urls = a[0]
var collection_urls = urls.split(",");
var to_email = a[1]
rimraf(directory, function () {
console.log("done");
});
fs.mkdirSync(directory);
for (url of collection_urls) {
await some_function_to_download(url); // 5 sec per download
}
await html_to_zip_file() // takes 5 sec to zip
.then(result => {
transporter.sendMail(set_mail_options(to_email)) //2 sec to send mail
.then(result => {
console.log("Mail sent")
})
.catch(err => {
console.log(err)
})
})
.catch(err => {
console.log(err)
})
console.log("reached") // this is reached before zip is done and mail sent. I want to prevent this
}
isFunctionRunning = false
}
You need to return transporter.sendMail in sendMail, fs.writeFile in someFunctionToDownload and zipper.zip in htmlToZipFile otherwise the await won't work as expected (I'm assuming that they actually do return promises, I'm only familiar with fs.writeFile)
Also: CamelCase is used in JS, not snake_case 🙃
And are you sure rimraf is synchronous?
const sendMails = async () => {
while (stack.length > 0) {
isFunctionRunning = true;
const [urls, toEmail] = stack.shift();
var collectionUrls = urls.split(",");
rimraf(directory, function() {
console.log("done");
});
await fs.mkdir(directory);
await Promise.All(collectionUrls.map(someFunctionToDownload)); // 5 sec per download
await htmlToZipFile() // takes 5 sec to zip
.then(result => transporter.sendMail(set_mail_options(toEmail))) //2 sec to send mail
.then(result => {
console.log("Mail sent");
})
.catch(err => {
console.log(err);
});
console.log("reached"); // this is reached before zip is done and mail sent. I want to prevent this
}
isFunctionRunning = false;
};
const someFunctionToDownload = async url => {
const a = url.split(".");
const googleIndexHTML = await getGoogleIndexHTML(url);
return fs.writeFile(`${directory}/${a[1]}.html`, googleIndexHTML, err => {
if (err) throw err;
});
};
const htmlToZipFile = async () => {
return zipper.zip(directory, function(error, zipped) {
if (!error) {
zipped.compress();
zipped.save("./package.zip", function(error) {
if (!error) {
console.log("Saved successfully!");
}
});
} else {
console.log(error);
}
});
};
Try using the following
while (stack.length > 0) {
isFunctionRunning = true
var a = stack.shift()
var urls = a[0]
var collection_urls = urls.split(",");
var to_email = a[1]
rimraf(directory, function () {
console.log("done");
});
fs.mkdirSync(directory);
for (url of collection_urls) {
await some_function_to_download(url); // 5 sec per download
}
try {
const result = await html_to_zip_file() // takes 5 sec to zip
const sendMailResult = await transporter.sendMail(set_mail_options(to_email))
} catch(e)
{
console.log(e)
}
console.log("reached")
}
Since html_to_zip_file() and sendMail function are independent
we can use
const result = await Promise.all([html_to_zip_file(),transporter.sendMail(set_mail_options(to_email))]);

how to let a function wait for result returned by another function?

i have a function called 'updateProfile()' which has a condition which is if(emailChangeConfirm), this condition depends upon the value of variable 'emailChangeConfirm' , this variable gets the value returned by another function called 'updateEmailAllProcessing()'
the condition 'if(emailChangeConfirm)' is not getting satisfied at all because compiler is not waiting for the function 'updateEmailAllProcessing()' to return the value for variable 'emailChangeConfirm'.
I used async/await for this but that is also not working as i want
Desired Solution :
function 'updateProfile()' must wait for the function 'updateEmailAllProcessing()' to get the result in 'emailChangeConfirm' so that i can enter in the condition 'if(emailChangeConfirm)'.
I am using typescript and working on hybrid app with ionic 3 and angular 5.
async updateProfile(updatedData : Credentials,tarUser : Credentials)
{
// console.log(tarUser,'<<--->>>',updatedData)
let count : number = undefined;
let emailChangeConfirm : boolean;
if(updatedData.name)
{
if(tarUser.name != updatedData.name)
tarUser.name = updatedData.name;
else
count++;
}
if(updatedData.email)
{
if(tarUser.email != updatedData.email)
{
**emailChangeConfirm = await this.updateEmailAllProcessing();**
console.log(emailChangeConfirm)
**if(emailChangeConfirm)
tarUser.email = updatedData.email;**
}
else
count++;
}
if(updatedData.phoneNo)
{
if(tarUser.phoneNo != updatedData.phoneNo)
tarUser.phoneNo = updatedData.phoneNo;
else
count++;
}
if(updatedData.photoURL)
{
if(tarUser.photoURL != updatedData.photoURL)
tarUser.photoURL = updatedData.photoURL;
else
count++;
}
if(count)
this.mesService.presentToast('Nothing Updated!!')
else **if(emailChangeConfirm)**
{
this.dbServe.editUser(tarUser).then(() =>
{
console.log("User Edited Successfully with email change too");
this.authServ.updateEmail(tarUser.email).then(() =>
{
console.log('login email updated');
this.authServ.logout();
})
//this.close();
})
}
else
{
this.dbServe.editUser(tarUser).then(() =>
{
console.log("User Edited Successfully with No email change");
this.close();
})
}
}
**async updateEmailAllProcessing()**
{
let result : boolean;
let alert = this.mesService.emailChangeConfirmation();
alert.present();
alert.onDidDismiss((data) => {
console.log('data->',data);
if(data)
{
let alert1 = this.mesService.passwordPrompt();
alert1.present();
alert1.onDidDismiss(data1 =>
{
console.log('password->',data1);
if(data1)
{
this.authServ.reauthenticateUser(data1).then(() =>
{
console.log('User Reauth Done');
result = true;
})
}
else
result = false;
})
}
else
result = false;
})
**return result;**
}
You need updateEmailAllProcessing to return a promise so you can await on it. and resolve the promise with the result inside the callback.
async updateEmailAllProcessing()
{
return new Promise((resolve, reject) => {
let result: boolean;
let alert = this.mesService.emailChangeConfirmation();
alert.present();
alert.onDidDismiss((data) => {
console.log('data->', data);
if (data) {
let alert1 = this.mesService.passwordPrompt();
alert1.present();
alert1.onDidDismiss(data1 => {
console.log('password->', data1);
if (data1) {
this.authServ.reauthenticateUser(data1).then(() => {
console.log('User Reauth Done');
resolve(true);
})
}
else
resolve(false);
})
}
else
resolve(false);
})
});
}

how to handle expressJs callback and how to update object's property inside a function?

I have two js files. i am able to get data from mongodb by calliing bookDao.getActiveBookByCategoryId().
My Problem
In categoryDao.js file i am trying to update resultJson.book_countinside BookDao.getActiveBookByCategoryId() method. but it is not updating. So may i know how to fix this.
here book_count property in resultJson is still 0.
categoryDao.js
module.exports.getAllActiveCategory = (callback) => {
Category.find({
is_delete : false
}, (error, result) => {
if(error) {
console.log(error);
callback(commonUtil.ERROR);
}
if(result) {
var categoryArray = [];
for(var i=0; i<result.length; i++) {
var categorySingle = result[i];
var resultJson = {
_id : categorySingle._id,
category_name : categorySingle.category_name,
created_on : categorySingle.created_on,
book_count : 0
}
BookDao.getActiveBookByCategoryId(categorySingle._id, (bookResult) => {
if(bookResult) {
if(bookResult.length > 0) {
resultJson.book_count = bookResult.length;
}
}
});
categoryArray.push(resultJson);
}
callback(categoryArray);
}
});
}
bookDao.js
module.exports.getActiveBookByCategoryId = (categoryId, callback) => {
Book.find({
is_delete : false,
category : categoryId
}, (error, result) => {
if(error) {
console.log(error);
callback(commonUtil.ERROR);
}
if(result) {
callback(result);
}
});
}
Try this, In your code categoryArray.push(resultJson); will not wait for BookDao.getActiveBookByCategoryId to finish because of async behavior.
module.exports.getActiveBookByCategoryId = (categoryId) => {
return Book.count({
is_delete: false,
category: categoryId
});
}
module.exports.getAllActiveCategory = async () => {
try {
// Find all category
const result = await Category.find({
is_delete: false
});
// Create array of promise
const promises = result.map(categorySingle => BookDao.getActiveBookByCategoryId(categorySingle._id));
// Get array of Category count
const data = await Promise.all(promises);
// update count in result
return result.map((categorySingle, i) => {
categorySingle.book_count = data[i];
return categorySingle;
});
} catch (error) {
console.log(error);
}
}

My arr is empty when I return it with a function

I'm trying to pass arrEntree in my return, however it's inside a promise. How would I fix this issue? The function query iniaites a new promise but like everytime ArrEntree is printed empty, because it gets called before the promise is finished processing. How would I finish the promise then only call my return...? How would I solve this issue.
function query(statement, params, first) {
return new Promise(function(resolve, reject) {
connection.query(statement, (params || []), function(err, rows) {
if (err) {
console.log("err", err);
if (err.code === "ETIMEDOUT") {
connectToDb();
return query(statement, params, first);
}
reject(err);
} else {
if (first) {
resolve(rows[0]);
} else {
resolve(rows);
}
}
});
});
}
function sendPlanEmails(){
if (plans.length === 0) {
return true;
}
const orders = plans.map(plan=>{
const arrEntree = []
const planData = srcData.plans[plan.productId];
const entrees = plan.entrees;
const entreeID = Object.keys(plan.entrees);
query('select * from entrees where entree_id in (' + entreeID.join(',') + ')')
.then(function(results){
results.forEach(function(element){
entreeID.forEach(function(second_elem){
if(element.entree_id==second_elem){
console.log('worked until here')
const quantity = plan.entrees[second_elem];
const name = element.entree;
arrEntree.push({name:name, quantity:quantity});
}
})
})
})
return {
days:plan.days.days,
planDataTitle:planData.title,
breakfast:plan.meals[0],
lunch:plan.meals[1],
dinner:plan.meals[2],
entreeArry:arrEntree
};
});
const template_mp = srcData.orderPage.mailchimp_template_mp || 'order_confirmation';
return utils.sendMealPlanEmail(template_mp, {
from: from,
order: orders,
});
}

Categories