Why is await code executing before sync code? - javascript

I have a signup user function that I dont really understand how it's working...
module.exports.signupUser = async (req, res) => {
let email = req.body.email;
let password = req.body.password;
if(!validator.isEmail(email))
{
res.status(400).json({
"message": "Please enter a valid email!"
}).end();
}
else if(!validator.isLength(password, {min:6})){
res.status(400).json({
"message": "Password must have at least 6 characters!"
}).end();
}
else {
const salt = bcrypt.genSaltSync(10);
const hashedPassword = await bcrypt.hash(password, salt);
let params = [email, hashedPassword];
console.log(hashedPassword);
let query = 'insert into users (email, password) values (?,?)';
connection.query(query, params, (err, result, fields) => {
if(err && err.code === 'ER_DUP_ENTRY') {
res.status(400).json({
"message": "There is an account already associated with this email adress!"
}).end();
}
else {
res.status(200).json({
"message": "User created!"
}).end();
}
});
}
}
So in the last else, I use await bcrypt.hash(password, salt) to encrypt my password.
I'm new to JS but still I understand that await executes code asyncronously, so console.log(hashedPassword) should not return me the hashed password because this log will execute before the actual hashing. Anyone can explain me what is really happening?

await executes the code synchronously. So in your case await will wait for the execution of bcrypt.hash.
For more clarity bcrypt.hash function returns a promise and now we have two choices here.
1. Await for the promise to resolve
const hashedPassword = await bcrypt.hash(password, salt);
console.log(hashedPassword);
the hashedPassword will contain the actual output we need.
2. Handle promise resolution using then
bcrypt.hash(password, salt).then(hashedPassword => {
console.log(hashedPassword);
})
As the bcrypt.hash function returns a promise, we need to handle the rest of the code inside a then block as it'll only return the value once the function resolves the promise.
In case you want to execute your code asynchronously, you need to remove the await keyword which waits for the promise to resolve before moving to the next line.

const hashedPassword = await bcrypt.hash(password, salt);
console.log(hashedPassword);
If you use the await keyword, nodejs will wait for promise to resolve before moving to the next line in the async function.
Your console.log's result will be the hashed password.
If you don't use the await keyword, node will start resolving the promise but will not wait for it to finish resolving before moving to next line.
Your console.log's result will be Promise { pending }

Related

Order of queries with the MongoClient in Node is changing the order of execution?

I'm trying to build a registration api end point and I'm using Mongodb in that call to check 2 things:
If the username that's passed in already exists in the DB then send a status 500
if the email that's passed in already exists in the DB then send a status 500
If none of those conditions get triggered, then insert: {"username": "foo", "email": "x#bar.com", "password": "123456"}
This is the endpoint I'm using:
const client = new MongoClient(url, { useUnifiedTopology: true });
router.use(express.json());
router.post('/register', (req, res, next)=>{
client.connect(async function(err, client) {
let db = client.db(dbName);
let users = 'users';
var status = 0;
await db.collection(users).findOne({'username': req.body.username}, (err, result) =>{
if(err) throw err;
if(result){
status = 1;
console.log(status)
console.log(`username exists!: ${result}`)
};
});
await db.collection(users).findOne({'email': req.body.email},(err, result) =>{
if(err) throw err;
if(result){
status = 2;
console.log(status)
console.log(`email exists!: ${result}`)
};
});
//catch any errors thrown from the DB
await db.collection(users).insertOne(
{'username': req.body.username, 'email': req.body.email, 'password': req.body.password}, (err, result)=>{
if(err) throw err;
});
console.log(`status: ${status}`);
if(status == 1){
res.status(500).send('username exists')
} else if(status == 2) {
res.status(500).send('email exists');
} else {
res.status(201).send('User created!');
}
});
});
And the output I keep getting is:
status: 0
1
username exists!: [object Object]
2
email exists!: [object Object]
Which confuses me since it seems to be completing the insert before the finds complete?
(And I'm wondering why printing the result is [object Object] when printing it?)
You are mixing asynchronous styles which is what is causing your issue. In older versions of node a callback style was used. When Promises became a standard feature many libraries updated their code to return a promise if and only if a callback was not passed to the function.
If you look at the documentation for find one at http://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#findOne you will see
Returns:
Promise if no callback passed
So, you either need to use callbacks properly and nest your calls within the callbacks or remove the callback arguments and use Promises + async/await proeprly.
For example using async await + promise value
const result = await db.collection(users).findOne({'username': req.body.username});
if (result){
status = 1;
console.log(status)
console.log(`username exists!: ${result}`)
};

Promise { <pending> } on bcrypt

I am trying to validate a user's password using bcryptjs. I have this function which returns a Promise, however when I get to bycrypt.hash, all i get is Promise { <pending> } Therefore .then() will not execute on undefined. Please help, I've been stuck on this a while
userSchema.methods.verifyPassword = function (password, err, next) {
const saltSecret = this.saltSecret;
const a = async function(resolve, reject) {
console.log('hi4')
console.log('this.saltSecret2', saltSecret);
console.log(password);
const hashed_pass = await bcrypt.hash(password, saltSecret);
console.log('hash', hashed_pass);
const valid = await bcrypt.compare(password, hashed_pass);
if(valid){
console.log('GOOD');
}
};
a();
};
I like to use async-await syntax to handle promises. It is less confusing. and gives the ability of quickly understanding someone else code.
you can make your function an async one. wait until bcrypt does its job
const password = await bcrypt.hash(password, saltSecret);
However bcrypt library provides a function to compare password and the hash
const valid = await bcrypt.compare(password, hashed_pass);
try this
async function(resolve, reject) {
console.log('hi4')
console.log(this.saltSecret);
console.log(password);
const hashed_pass = await bcrypt.hash(password, saltSecret);
console.log('hash', hashed_pass);
const valid = await bcrypt.compare(password, hashed_pass);
if(valid){
console.log('GOOD');
}
};
This line will always return a Promise.
console.log(bcrypt.hash(password, this.saltSecret));
You could always do something like this.
return new Promise(async (resolve, reject) => {
const hash = await bcrypt.hash(password, this.saltSecret);
if (hash == this.password) {
return resolve(true);
}
return reject();
});
bcrypt.hash uses a callback, not a promise (that's what the .then is doing)
You should use it like so:
bcrypt.hash(password, this.saltSecret, (err, hash) => {
...
});

Call a function inside async function at a js class

Hi I'm new at javascript programming.
I have a node express project, I'm trying to create a login method inside my AuthenticationController class.
My login method is like this right now:
const User = require('../models/User')
class AuthenticationController {
async login(req, res) {
const { email, password } = req.body
console.log('step 1')
var hashPassword = await userPassword(email)
console.log(hashPassword)
console.log('step 2')
return res.status(200).json({ 'msg': 'Log in OK!' })
}
userPassword(email) {
User.findOne({ email: email }).exec(function(err, user) {
if (err) return err
else return user.password
})
}
}
But I got an error saying that userPassword is undefined, I couldn't figure out why. So my doubts are: why this is happening, and how to do it correctly ?
I also checked out this questions, but they didn't helped me:
How to call an async function
Async function inseide a class
The error message at my console:
(node:28968) UnhandledPromiseRejectionWarning: ReferenceError: userPassword is not defined
...
(node:28968) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:28968) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
login doesn't refer to userPassword method but to the function of the same name which doesn't exist.
Promises are supposed to be be chained and they aren't. userPassword is expected to return a promise but it uses obsolete Mongoose callback API.
That UnhandledPromiseRejectionWarning is shown means that errors weren't correctly handled in login while they should. As explained in this answer, Express don't support promises so errors should be handled by a developer.
It should be:
async login(req, res) {
try {
const { email, password } = req.body
var hashPassword = await this.userPassword(email)
return res.status(200).json({ 'msg': 'Log in OK!' })
} catch (err) {
// handle error
}
}
async userPassword(email) {
const { password } = await User.findOne({ email: email });
return password;
}
this error is coming because you are not handling error for the promise. Always use async/await inside try/catch block.
try{
async login(req, res) {
const { email, password } = req.body
console.log('step 1')
var hashPassword = await userPassword(email)
console.log(hashPassword)
console.log('step 2')
return res.status(200).json({ 'msg': 'Log in OK!' })
}
}catch(e){
console.log(e)
}

Async/Await on return variable from promise

I've got into the pattern of using async await in my aws nodejs lambda functions, and I common thing I run into is the need to await the result of a promise and use the response in the next async/await promise, and sort of repeat this pattern until I've run all my logic.
let userId;
await GetUserId({AccessToken: headers.accesstoken}).then(function(res){
userId = res;
},function(err){
});
let username;
await GetUserName(userId).then(function(res){
username = res;
},function(err){
});
Is there anyway I can declare and assign userId a value in the same line as invoking the function.
sudo code:
let userId = await GetUserId().then(()=>{ //bubble response up to userId })
The reason I'm asking is that it just sort of messing/wrong initializing a variable separately. Maybe I need a different pattern, or it's just something I'll have to live with.
Solution
var ExampleFunction = async() => {
try {
const userId = await GetUserId('token');
const username = await GetUserName(userId);
console.log(`userId: ${userId}`);
console.log(`username: ${username}`);
} catch (err) {
console.log(err);
console.log(`Exit Function`);
}
function GetUserId(token) {
return new Promise(function(resolve, reject) {
if (!token)
reject('no token');
resolve('ID');
});
}
function GetUserName(userId) {
return new Promise(function(resolve, reject) {
if (!userId)
reject('no userId');
resolve('NAME');
});
}
}
ExampleFunction();
The await is supposed to replace the then syntax (except you really need to distinguish fulfillment from rejection with it). The await expression either throws the rejection reason as an exception to catch, or results in the fulfilment value of the promise. You can directly use that:
const userId = await GetUserId({AccessToken: headers.accesstoken});
const username = await GetUserName(userId);
(I assume that it was unintentional that your current code ignored errors and continued with undefined values).
The keyword await makes JavaScript wait until that promise resolves and returns its result.
let userId = await GetUserId({AccessToken: headers.accesstoken});
if you assign the result of await to var it will be the promise resolve value
so you can
let userId = await GetUserId({AccessToken: headers.accesstoken});
and
let username = await GetUserName(userId);
PS. don't forget error handling using try/catch.

Trying to hash a password using bcrypt inside an async function

Following on from this question.
I feel like I'm almost there, but my incomplete understanding of async is preventing me from solving this. I'm basically trying to just hash a password using bcrypt and have decided to seperate out the hashPassword function so that I can potentially use it in other parts of the app.
hashedPassword keeps returning undefined though...
userSchema.pre('save', async function (next) {
let user = this
const password = user.password;
const hashedPassword = await hashPassword(user);
user.password = hashedPassword
next()
})
async function hashPassword (user) {
const password = user.password
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds, function(err, hash) {
if (err) {
return err;
}
return hash
});
return hashedPassword
}
await dosent wait for bcrypt.hash because bcrypt.hash does not
return a promise. Use the following method, which wraps bcrypt in a promise in order to use await.
async function hashPassword (user) {
const password = user.password
const saltRounds = 10;
const hashedPassword = await new Promise((resolve, reject) => {
bcrypt.hash(password, saltRounds, function(err, hash) {
if (err) reject(err)
resolve(hash)
});
})
return hashedPassword
}
Update:-
The library has added code to return a promise which will make the use
of async/await possible, which was not available
earlier. the new way of usage would be as follows.
const hashedPassword = await bcrypt.hash(password, saltRounds)
By default, bcrypt.hash(password,10) will return as promise. please check here
Example: Run the code,
var bcrypt= require('bcrypt');
let password = "12345";
var hashPassword = async function(){
console.log(bcrypt.hash(password,10));
var hashPwd = await bcrypt.hash(password,10);
console.log(hashPwd);
}
hashPassword();
Output:
Promise { <pending> }
$2b$10$8Y5Oj329TeEh8weYpJA6EOE39AA/BXVFOEUn1YOFC.sf1chUi4H8i
When you use await inside the async function, it will wait untill it get resolved from the promise.
use The method bcrypt.hashSync(), It is Synchronous out of the box.
const hashedPassword = bcrypt.hashSync(password,saltRounds);
Hashing bcrypt asynchronously should be like this
bcrypt.hash(password, saltRounds, function(err, hash) {
if (err) {
throw err;
}
// Do whatever you like with the hash
});
If you are confused with sync and async. You need to read more about them.
There are a lot of good articles out there.
You need to look here in the documentation.
Async methods that accept a callback, return a Promise when callback
is not specified if Promise support is available.
So, if your function call takes in a callback then you can't use await on it since this function signature doesn't return a Promise. In order to use await you need to remove the callback function. You can also wrap it in a Promise and await on it but that's a bit overkill since the library already provides a mechanism to do so.
Code refactor:
try {
// I removed the callbackFn argument
const hashedPassword = await bcrypt.hash(password, saltRounds)
} catch (e) {
console.log(e)
}
const hashedPassword = (password, salt) => {
return new Promise((resolve, reject) => {
bcrpyt.hash(password, salt, (err, hash) => {
if (err) reject();
resolve(hash);
});
});
};
hashedPassword('password', 10).then((passwordHash) => {
console.log(passwordHash);
});
Had same issue...
solved by assigning the Boolean value from a function:
compareHash = (password, hashedPassword) => {
if (!password || !hashedPassword) {
return Promise.resolve(false);
}
return bcrypt.compare(password, hashedPassword);
};
Here the 2 arguments will not be undefined, which is the cause of issue.
And calling the function:
let hashCompare = this.compareHash(model.password, entity.password);

Categories

Resources