This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I'm getting the required result from console.log in getuser(), but I'm getting undefined when used it in another function
let getuser = (id) => {
user.findById(id, (err, user) => {
if (err || !user) {
return "wrong user id";
} else {
let { name, _id } = user;
console.log(JSON.stringify({ name, _id }));
return { name, _id };
}
});
};
I'm getting undefined when i use getuser() in send()
exports.send = (req, res) => {
let {id}=req.body
let a=getuser(req.User._id)
let b=getuser(req.body.id)
console.log(a)
console.log(a)
let message = new messages({
user1: a,
user2: b,
});
message.save((err, saved) => {
if (err) {
return res.json(err);
} else {
return res.json(saved);
}
});
}
im getting undefined in
console.log(a)
console.log(a)
You probably want to use a Promise for the getuser method:
let getuser = (id) => new Promise((resolve, reject) => {
user.findById(id, (err, user) => {
if (err || !user) {
reject("wrong user id");
} else {
let { name, _id } = user;
console.log(JSON.stringify({ name, _id }));
resolve({ name, _id });
}
});
});
The operation looks asynchronous, so you need to wait for the result before continuing. This means that your getuser calls need to handle promise/async values:
let a = await getuser(req.User._id)
let b = await getuser(req.body.id)
Of course, await must be used in a function marked with async. Read up on Promises and async/await if any of this is unclear - they're absolutely imperative for working with databases, APIs etc.. It may be possible to use it immediately by changing your exports line:
exports.send = async (req, res) => {
But I don't know what environment this is for, so I can't say if that will work out of the box.
Please note that I haven't tested this at all, as it's a rather incomplete example to begin with. What I've suggested is merely theoretical.
Related
I am trying to get a value returned from a function where I read and write a file using fs.readFile/writeFile in Node.
In my main server.js file, a request comes in and I then want to send an email from another file called sendEmail.js:
const fs = require('fs')
const sendMail = require('./sendEmail')
async function sendAnEmail() {
let resultOfSend = await sendMail.sendEmail()
resultOfSend.then((result)=>{
// return the result
}
}
sendAnEmail();
In sendEmail I first read a file to get the email to send to,
then write to a second file
then, if all is good, I send an email (from a separate function):
async function sendEmail() {
// Check if user exists
fs.readFile('./file.json', (err, data) => {
if(err) {
throw error
}
else {
let users = JSON.parse(data)
let dataToWrite = JSON.stringify(users)
fs.writeFile('./file2.json', dataToWrite, (err) => {
if(err) {
console.error(err)
throw error
}
else {
return generateEmail(users)
.then((info) => {
return info
})
.catch(console.log('err'))
}
})
}
})
}
async function generateEmail(user) {
let msgText = 'hello world'
// Set the mail options
const mailOptions = {
...
}
// Send the mail
let info = await transporter.sendMail(mailOptions)
return info
}
module.exports = {sendEmail}
What I can't get is a value for the resultOfSend variable. Keeps coming back undefined, I think because the promise hasn't yet been fulfilled.
How do I get a value to return from the sendEmail function back to the server.js file where it's called from?
You're using await and async in sendEmail but not returning any Promise (So the sendEmail function doesn't return anything and this is why you get undefined).
Nevertheless, on the response you're trying to call .then() even though you used await.
So you should:
return Promise in the sendEmail function.
decide how you want to handle it, if you use async-await then dont use .then() and just analyze the variable and vice versa.
generateEmail() function should also return Promise.
For example:
async function sendEmail() {
return new Promise((resolve, reject) => {
// Check if user exists
fs.readFile('./file.json', (err, data) => {
if(err) {
reject()
}
else {
let users = JSON.parse(data)
let dataToWrite = JSON.stringify(users)
fs.writeFile('./file2.json', dataToWrite, (err) => {
if(err) {
console.error(err)
reject()
}
else {
generateEmail(users)
.then((info) => {
resolve(info)
})
.catch(
console.log('err')
reject()
)
}
})
}
})
})
}
I have a method that selects distinct values from a database as shown below:
function displayCategories(res, req) {
query = `SELECT DISTINCT name FROM product_category;`;
connection.query(query, function (err, rows) {
if (err) {
console.log(err);
res.render("home");
throw err;
} else {
session = req.session;
session.categories = rows[0];
}
});
}
I then have a button with the method POST and action /categories
The displayCategories is called when the button is clicked as follows:
router.post('/categories', function (req, res) {
displayCategories(res, req);
if (session.categories === undefined) {
console.log("categories is undefined");
} else {
console.log("categories is defined");
console.log(session.categories);
}
})
I added some console logs for test purposes. The issue I am having is that the first time I click the button, it returns undefined. Each time I click it again, it prints the correct data for session.categories as shown below:
Is there a simple fix for this issue?
The code is calling a displayCategories as if it were synchronous, but it is running asynchronous code with the callback.
There are multiple possible solutions for that but one of them would be to use Promises, like the following:
const displayCategories = (res, req) => new Promise((resolve, reject) => {
// you are not declaring query in this scope, that makes it global
query = `SELECT DISTINCT name FROM product_category;`
connection.query(query, function (err, rows) {
if (err) {
console.error(err)
res.render("home")
reject(err)
} else {
session = req.session
session.categories = rows[0]
resolve()
}
})
})
And the other part with an async function
router.post('/categories', async function (req, res) {
await displayCategories(res, req);
if (session.categories === undefined) { // session is not declared
console.log("categories is undefined");
} else {
console.log("categories is defined");
console.log(session.categories); // session is not declared
}
})
But that's just to make your issue go away, if you want to improve the code even further you can just keep the responsibility of dealing with request and response with the controller action and just use the other function to get the data you want, isolation its responsibility:
const getCategories = () => new Promise((resolve, reject) => {
const query = `SELECT DISTINCT name FROM product_category;`
connection.query(query, (err, rows) => {
if (err) return reject(err)
resolve(rows)
})
})
router.post('/categories', async function (req, res) {
try {
req.session.categories = await getCategories();
if (req.session.categories === undefined) {
console.log("categories is undefined");
} else {
console.log("categories is defined", req.session.categories);
console.log();
}
} catch(e) {
console.error(e)
res.render("home")
}
})
I am currently trying to do a get request in my NodeJS API, get some data and return the modified value.
From what I read in other similar questions is that you cannot just return the modified object but you need to use a callback function or a promise in order to return it. I have a standard MVC pattern where I use a controller, service.
Here is my service:
const rp = require('request-promise');
exports.RequestUserPermissions = async function(role, next) {
try {
await rp('https://api.myjson.com/bins/7jau8').then(response => {
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
});
} catch (error) {
console.log(error);
next(error);
}
};
Here is my controller:
const UserPermissionsService = require('../services/userPermissions.service');
exports.getUserPermissions = async function(req, res) {
try {
const role = req.headers.role; // console.log(req.headers.role);
const loggedInUserPermissions = await UserPermissionsService.RequestUserPermissions(role);
return res.status(200).json({ status: 200, data: loggedInUserPermissions, message: 'User permissions retrieved.' });
} catch (error) {
throw Error(error, 'error inside the get.user.permissions function');
}
};
So my issue is that I'm trying to return the value of filteredPermissions to my controller but I keep getting undefined. Which I guess it's a async - await issue. Meaning that the function ends before I make my calculations.
I originally had my service as:
await request.get('https://api.myjson.com/bins/7jau8', (error, response, body) => {
if (!error && response.statusCode === 200) {
const permissionsResponse = JSON.parse(body);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
return permissionsResponse;
} else {
console.log('Got an error:', error);
}
});
but I changed it to use the request-promise module, so that I can return my response. What am I doing wrong ? How can I pass my calculations properly??
Change this:
await rp('https://api.myjson.com/bins/7jau8')
to this:
return rp('https://api.myjson.com/bins/7jau8')
You need to be returning something useful from your exports.RequestUserPermissions function. As it stands now, there's no return value from that function which means the promise it returns will just have an undefined resolved value which is apparently what you are experiencing.
Then, I'd suggest using a .catch() for the error condition. And, you need to allow the caller to see the error (probably as a rejected promise) so it can know when there's an error.
I would suggest this:
const rp = require('request-promise');
exports.RequestUserPermissions = function(role, next) {
return rp('https://api.myjson.com/bins/7jau8').then(response => {
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
}).catch(error => {
console.log(error);
next(error);
throw error;
});
};
The spec for exactly what you want is a bit confused. To be able to test things with the URL you gave me, I created a simple stand-alone node program here. This looks for one matching role and returns that. If no matching role is found, it resolves to null. You could also make that reject, depending upon how the caller wants no matching role to work.
const rp = require('request-promise');
function getRole(role) {
return rp({uri: "https://api.myjson.com/bins/7jau8", json: true}).then(data => {
// need to find the matching role
// apparently role can be a string or an array of strings
for (let item of data) {
if (item[role]) {
return item[role];
}
}
return null;
});
}
getRole("admin").then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
When, I run this, I get this output:
{ static:
[ 'posts:list',
'posts:create',
'posts:edit',
'posts:delete',
'users:get',
'users:getSelf',
'home-page:visit',
'dashboard-page:visit' ]
}
Hopefully, can you take this an modify to fit your other needs.
Note: I'm using the json:true option with rp() so it will parse the JSON response for me automatically.
If you are using async/await with request-promise then you don't need to call .then(), you can just assign your rp call directly to a variable. For example this:
await rp('https://api.myjson.com/bins/7jau8').then(response => {
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
});
Would become this:
const response = await rp('https://api.myjson.com/bins/7jau8');
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
I'm trying to have a loop with some db calls, and once their all done ill send the result. - Using a promise, but if i have my promise after the callback it dosent work.
let notuser = [];
let promise = new Promise((resolve, reject) => {
users.forEach((x) => {
User.find({
/* query here */
}, function(err, results) {
if(err) throw err
if(results.length) {
notuser.push(x);
/* resolve(notuser) works here - but were not done yet*/
}
})
});
resolve(notuser); /*not giving me the array */
}).then((notuser) => {
return res.json(notuser)
})
how can i handle this ?
Below is a function called findManyUsers which does what you're looking for. Mongo find will return a promise to you, so just collect those promises in a loop and run them together with Promise.all(). So you can see it in action, I've added a mock User class with a promise-returning find method...
// User class pretends to be the mongo user. The find() method
// returns a promise to 'find" a user with a given id
class User {
static find(id) {
return new Promise(r => {
setTimeout(() => r({ id: `user-${id}` }), 500);
});
}
}
// return a promise to find all of the users with the given ids
async function findManyUsers(ids) {
let promises = ids.map(id => User.find(id));
return Promise.all(promises);
}
findManyUsers(['A', 'B', 'C']).then(result => console.log(result));
I suggest you take a look at async it's a great library for this sort of things and more, I really think you should get used to implement it.
I would solve your problem using the following
const async = require('async')
let notuser = [];
async.forEach(users, (user, callback)=>{
User.find({}, (err, results) => {
if (err) callback(err)
if(results.length) {
notUser.push(x)
callback(null)
}
})
}, (err) => {
err ? throw err : return(notuser)
})
However, if you don't want to use a 3rd party library, you are better off using promise.all and await for it to finish.
EDIT: Remember to install async using npm or yarn something similar to yarn add async -- npm install async
I used #danh solution for the basis of fixing in my scenario (so credit goes there), but thought my code may be relevant to someone else, looking to use standard mongoose without async. I want to gets a summary of how many reports for a certain status and return the last 5 for each, combined into one response.
const { Report } = require('../../models/report');
const Workspace = require('../../models/workspace');
// GET request to return page of items from users report
module.exports = (req, res, next) => {
const workspaceId = req.params.workspaceId || req.workspaceId;
let summary = [];
// returns a mongoose like promise
function addStatusSummary(status) {
let totalItems;
let $regex = `^${status}$`;
let query = {
$and: [{ workspace: workspaceId }, { status: { $regex, $options: 'i' } }],
};
return Report.find(query)
.countDocuments()
.then((numberOfItems) => {
totalItems = numberOfItems;
return Report.find(query)
.sort({ updatedAt: -1 })
.skip(0)
.limit(5);
})
.then((reports) => {
const items = reports.map((r) => r.displayForMember());
summary.push({
status,
items,
totalItems,
});
})
.catch((err) => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
}
Workspace.findById(workspaceId)
.then((workspace) => {
let promises = workspace.custom.statusList.map((status) =>
addStatusSummary(status)
);
return Promise.all(promises);
})
.then(() => {
res.status(200).json({
summary,
});
})
.catch((err) => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
};
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
Is there a way to call function that contains mongoose query inside other function, so other function will work properly?
My first function containing mongoose query:
getUserTags = (id) => {
User.findById(id)
.exec( (error, user) => {
if (error) {
return next(error);
} else {
return user;
}
})
}
and my functions that needs to call that first function:
userTagToBookTagValues = (id) => {
const user = getUserTags(id);
//I NEED THIS PART TO WORK AFTER getting data from getUserTags
console.log(user);
user.tags.forEach(tag => {
console.log(tag)
});
}
Is there a way so it works properly and user in second function will not be undefined?
You maybe need to return User.findById?
Like so:
getUserTags = (id) => {
return User.findById(id)
.exec( (error, user) => {
if (error) {
return next(error);
} else {
return user;
}
})
}
As I understand .exec() do not return anything, so using .then gives error.
However if rewritten like this, it works perfectly:
getUserTags = (id) => {
return User.findById(id)
}
userTagToBookTagValues = (id) => {
const user = getUserTags(id).then((user) => {
user.tags.forEach(tag => {
console.log(tag)
});
}) ;
}
findById is an asynchronous method.
If your node version greater then 8
getUserTags = id => User.findById(id).exec();
userTagToBookTagValues = async (id) => {
try {
const user = await getUserTags(id);
user.tags.forEach(tag => {
console.log(tag)
});
} catch (err) {
next(err);
}
}
Or you can use then method after calling getUserTags function.
For more information about of asnc methods:
Medium