why is await not working even when used in async function - javascript

I made a function to search a user by email id. I'm calling that function in an async function using await and assigning the returned value to or constant/variable but getting undefined on printing the constant/variable
function search(email) {
sql = `SELECT email FROM users WHERE email = '${email}'`;
db.query(sql, (err, res) => {
if (err) {
console.log(err);
}
else {
return res[0].email;
}
})
}
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer', '');
const decoded = jwt.verify(token, 'catisdoguniversaltruth');
const user = await search(decoded._id);
console.log(user);
if (!user) {
throw new Error();
}
next();
}
catch (e) {
res.status(401).send("Not Authenticated, Please login");
}
};
module.exports = auth;

You need search() to be a promise and not a function.
await waits for a promise to resolve.
Try this:
function search(email) {
return new Promise((resolve, reject) => {
sql = `SELECT email FROM users WHERE email = '${email}'`;
db.query(sql, (err, res) => {
if (err) {
reject(err);
}
else {
resolve(res[0].email);
}
})
})
}
This will be resolved as promise and auth() will wait.
You could also build search() as async/await promise. Doesn't really matter as long as you return a promise resolve.

Related

Method returns undefined the first time it is called

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")
}
})

Returning undefined result after awaiting function

I am trying to get the result back from function where the result is in the callback. However the result always come back as undefined even though I set it to be the res. Can someone explain why this does not work?
function getData(id, callback) {
return dataModel.findOne({ id }).exec((err, res) => {
if (err) return callback(err)
return callback(null, res)
})
}
async function middleware(req.body) {
try {
let result
await getData(req.body, (err, res) => {
if (err) throw err
result = res
})
await nextFunc(result)... // result is undefined
} catch (err) {
console.log(err)
}
}
You are using callbacks, but in order to make things work with async await you are supposed to use promises
Try
function getData(id, callback) {
return new Promise((resolve, reject) => {
dataModel.findOne({ id }).exec((err, res) => {
if (err) reject(err);
resolve(res);
});
});
}
async function middleware(req.body) {
try {
let result = await getData(req.body);
await nextFunc(result);
} catch (err) {
console.log(err);
}
}

Async and promise function do not display anything in my res.json call

I have a controller in javascript which should get a given user, and then the pets that are associated with the user. The related pets are stored in an array of object refs within the user schema. At the minute, when I try to res.json the resulting array containing the related pets, it outputs as an empty array '[]'. Following the Mozilla docs and tutorials I have tried to implement a promise on this function to combat my previous issue of the res.json outputting an empty array. I'm not sure where I am going wrong as I am a newbie to JS/express/node/mongo
Problem code:
export const getPetsForAUser = (req, res)=>
{
function getter(){
return new Promise(resolve =>{
User.findOne({_id: req.params._id}, (err, users) =>{
let petlist = users.pets;
for(var i = 0; i < petlist.length; i++){
Pet.findOne({_id:petlist[i]}, (err, pet) =>{
var t = pet
return Promise.resolve(t)
});
}
})
});
}
async function asyncCall(){
const result = await getter();
res.json(result);
}
asyncCall();
};
Using Aync/Await and Promise all
export default async (req, res) => {
const promises = [];
let result = null;
const petlist = await new Promise((resolve, reject) => {
User.findOne({ _id: req.params._id }, (err, users) => {
if (err) {
reject(err);
} else {
resolve(users.pets);
}
});
});
if (petlist && petlist.length) {
for (let i = 0; i < petlist.length; i++) {
// eslint-disable-next-line no-loop-func
const promise = new Promise((resolve, reject) => {
Pet.findOne({ _id: petlist[i] }, (err, pet) => {
if (err) {
reject(err);
} else {
resolve(pet);
}
});
});
promises.push(promise);
}
result = await Promise.all(promises).then((data) => {
console.log('all promises resolved!');
console.log(data);
return data;
});
}
console.log(result);
};
You can implement promises like this in your code:
export const getPetsForAUser = (req, res) => {
return new Promise((resolve, reject) =>{
User.findOne({_id: req.params._id}, (err, users) => {
if (err) reject(err);
let petlist = users.pets;
for(var i = 0; i < petlist.length; i++) {
Pet.findOne({_id:petlist[i]}, (err, pet) =>{
if (err) reject(err);
var t = pet
resolve(t)
});
}
})

Express: Resolved Promise going to catch Call

I am trying to send the data from a promise, using Promise chaining the Promise is getting resolved. But the catch call is running not then call
Here is the code from where I am returning the data using Promise:
UserSchema.statics.findByCredentials = function(email, password) {
const User = this;
return User.findOne({
email
}).then((user) => {
if(!user) {
return Promise.reject();
}
try {
return new Promise((reject, resolve) => {
bcrypt.compare(password, user.password, (err, res) => {
// console.log(res);
if(res)
resolve(user);
else
reject("Invalid Credentials");
});
});
}
catch(error) {
Promise.reject(error);
}
});
};
Here is the Promise chain call, where catch call is working not the then call
app.post('/users/login', (req, res) => {
const body = _.pick(req.body, ['email', 'password']);
// console.log(body);
User.findByCredentials(body.email, body.password)
.then((user) => {
}).catch(error => {
error.generateAuthToken().then((token) => {
res.header('x-auth',token).send(error);
});
});
});
Hi tweaked the code, to have another call on the catch.
Could be there any better solution for that.
It's sure that the data is being returned that means, Promise is resolved.

Node.js Mongoose Promise getting lost

I have a Node.js API with a mongoDB. There is a route that creates a user and needs to hash the password, for this I use the bcryptjs package.
the route looks like this:
router.route('/user')
.post(function(req, res) {
if(req.body.password === req.body.passwordConfirm) {
userManager.addUser(req.body)
.then(function(response) { // waiting for the result of the mongoDB save
res.send({data:response});
});
} else {
res.send({err:'passwords do not match'});
}
})
and userManager.addUSer:
this.addUser = function(userobject) {
bcrypt.genSalt(10, function(err, salt) { // generate a salt
if(err !== null) {
console.log(err);
} else {
bcrypt.hash(userobject.password_hash, salt, function(err, hash) { // hash pw
if(err !== null) {
console.log(err);
else {
userobject.password_hash = hash; // store hash in user obj
var user = new User(userobject);
return user.save().catch(function(err){ // save user in mongoDB
console.log(err);
});
}
});
}
});
};
I get an error saying: "Cannot read property 'then' of undefined", which tells me that I am not receiving a promise from addUser. I looked and bcryptjs sadly does not use promises, however, mongoose does.
(adding this:
var mongoose = require('mongoose').Promise = Promise;
didn't help)
I tried wrapping the function in a promise with reject and resolve, but that gives this error: "TypeError: Promise resolver undefined is not a function".
How do I get the promise that the save() function of mongoose returns back to the .then() in the post route? I tried adding return in front of the two bcrypt function but that didn't work either..
Any suggestions are welcome!
Your addUser function nevers returns the promise to its caller. You're doing a return from the bcrypt.hash callback function, but that has nothing to do with addUser's return value.
It looks like addUser has to use some non-Promise-enabled APIs, so you're stuck with doing new Promise, something like this (see the *** comments):
this.addUser = function(userobject) {
return new Promise(function(resolve, reject) { // ***
bcrypt.genSalt(10, function(err, salt) { // generate a salt
if(err !== null) {
reject(err); // ***
} else {
bcrypt.hash(userobject.password_hash, salt, function(err, hash) { // hash pw
if(err !== null) {
reject(err); // ***
else {
userobject.password_hash = hash; // store hash in user obj
var user = new User(userobject);
resolve(user.save()); // *** save user in mongoDB
}
});
}
});
});
};
Also note that I don't have addUser just swallowing errors; instead, they're propagated to the caller. The caller should handle them (even if "handling" is just logging).
You do not return a Promise form your this.addUser, you have to convert your callback based bcrypt to Promises. You can convert the whole bcrypt API to support Promise based functions using e.g. promisifyAll of the bluebird library, or do it manually using new Promise like this way:
this.addUser = function(userobject) {
return new Promise((resolve, reject) => {
bcrypt.genSalt(10, (err, salt) => {
if (err) {
reject(err);
} else {
bcrypt.hash(userobject.password_hash, salt, function(err, hash) {
if (err) {
reject(err)
} else {
resolve(hash)
}
})
}
});
})
.then(hash => {
userobject.password_hash = hash; // store hash in user obj
var user = new User(userobject);
return user.save() // save user in mongoDB
})
.catch(function(err) {
console.log(err);
});
}
Or that way:
this.addUser = function(userobject) {
return new Promise((resolve, reject) => {
bcrypt.genSalt(10, (err, salt) => {
if (err) {
reject(err);
} else {
resolve(salt);
}
});
})
.then(salt => {
return new Promise((resolve, reject) => {
bcrypt.hash(userobject.password_hash, salt, function(err, hash) {
if (err) {
reject(err)
} else {
resolve(hash)
}
})
})
})
.then(hash => {
userobject.password_hash = hash; // store hash in user obj
var user = new User(userobject);
return user.save() // save user in mongoDB
})
.catch(function(err) {
console.log(err);
});
}
After doing some more digging in the change logs of bcryptjs I found out that they added promises but did not update the documentation.. The genSalt en hash methods will return a promise if the callbacks are omitted. This would translate to:
this.addUser = function(userobject) {
return bcrypt.genSalt(10).then((salt) => {
return bcrypt.hash(userobject.password, salt).then((hash) => {
userobject.password_hash = hash;
var user = new User(userobject);
return user.save();
});
});
};

Categories