Javascript - await triggering at wrong time - javascript

I'm very new to async and await, I'm hoping someone can help me out.
I have a function call register and within that function I register a user, and then send some data about them to the server to build a "user profile" so to speak.
Problem I'm having is that I also have a function called login and that is also async and redirects the user as soon as they are registered.. meaning the "user profile" data never gets sent.
Here is my register function:
async register(user: User) {
try {
const result = await this.afAuth.auth.createUserWithEmailAndPassword(user.email, user.password);
await this.afAuth.auth.currentUser.updateProfile({
displayName: user.displayName,
photoURL: ""
}).then(() => {
let currentUser = result;
let date = new Date().getTime();
let userData = {
email: currentUser.email,
displayName: currentUser.displayName,
uid: currentUser.uid,
created: date,
}
this.database.list('users').set(userData.uid, userData).then(() => {
return;
}); //I want to continue after this line is called.
return;
}, function(error) {
console.log(error)
});
} catch(e) {
console.log(e);
}
}
Is my await in the wrong location? I want the login function to get called once the data is .set...
Any ideas what I'm doing wrong.. I'd really appreciate any help. Thanks!

The point of using async / await is that you don't have to deal with then() or catch().
async function register(user: User) {
try {
const result = await this.afAuth.auth.createUserWithEmailAndPassword(user.email, user.password);
await this.afAuth.auth.currentUser.updateProfile({
displayName: user.displayName,
photoURL: ""
});
let currentUser = result;
let date = new Date().getTime();
let userData = {
email: currentUser.email,
displayName: currentUser.displayName,
uid: currentUser.uid,
created: date,
};
await this.database.list('users').set(userData.uid, userData)
// Do something after everything above is executed
return;
} catch(e) {
console.log(e);
}
};

await lets you wait on a promise. One of the largest benefits of async-await is not having to use then syntax or any explicit promises. Not sure why you mix styles. It is extremely simple to 'do more stuff' after an await statement.
Here is some pseudocode. I took out the try catch because your entire function is in it, which makes it rather pointless. Also, if any promise rejects, it is translated into an exception. If an exception occurs within a promise, it is translated into a rejection which is then translated into an exception. Other exceptions outside of promises are plain old exceptions.
I took the liberty of adding the TypeScript tag to your question because you posted TypeScript syntax. TypeScript is not Javascript.
async register(user: User) {
const result = await createUser(...);
await updateProfile(...);
const userData = buildUserData(...);
await setUserData(database, userData);
console.log('more stuff after setUserData');
}

Related

Authenticate function not waiting for promise to resolve

I'm making an authenticate function. It actually works with hardcoded users, but when I start getting users from Firebase, things start getting asynchronous and issues with timing start happening.
I've got a kind of long-winded Javascript function here that I believe returns a promise.
function authenticate({ username, password }) {
return users.then((querySnapshot) => {
return querySnapshot.forEach(doc => {
let user = doc.data();
if (user.username.toUpperCase() == username.toUpperCase())
return bcrypt.compare(password, user.password).then(function (result) {
console.log(password);
console.log(user.password);
console.log(result);
if (result) {
const token = jwt.sign({ sub: user.id }, config.secret);
const { password, ...userWithoutPassword } = user;
return {
...userWithoutPassword,
token
};
}
})
})
})
}
Console logging seems to confirm that this is a promise. I'll be honest, I copy-pasted a lot of the code inside, so I'm still not entirely sure how it works, but the promise syntax is at least something I'm confident in. After I go through a list of users pulled from Firebase and check that both username and password match, the guts of if (result) should run. result does come back as true, which is correct for what I'm trying to log in with, but my password form rejects me because it continues processing before the authenticate method is finished.
In another Javascript file, I have the method that calls this one.
function authenticate(req, res, next) {
console.log(req.body);
userService.authenticate(req.body)
.then(user => console.log(user))
//.then(user => user ? res.json(user) : res.status(400).json({ message: 'Username or password is incorrect' }))
.catch(err => next(err));
}
I'm learning a lot about asynchronous programming recently but this is defying my expectations a bit. Surely doing .then() on authenticate() should run authenticate(), get a promise, even if it's unresolved, then wait for it to resolve before executing the rest of the statements? The current issue is that the method goes ahead, finds no value for user, then throws a 400, which I think is an issue with asynchronicity. Can anyone explain why the outer authenticate function isn't waiting and how I could make it do that?
There are two possible issues:
Result of forEach
The forEach function returns undefined, see Array.prototype.forEach(). If you need the result of the iteration, you can use Array.prototype.map()
Waiting for the Promise
The following statement sounds like the code does not await the result properly:
my password form rejects me because it continues processing before the authenticate method is finished.
If you have a javascript promise, you can use the await keyword in order to continue the function execution only if the promise is either resolved or rejected. Have a look at the examples here: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/await
In your example, the authenticate function would look like this:
async function authenticate(req, res, next) {
console.log(req.body);
await userService.authenticate(req.body)
.then(...)
.catch(...);
}
Note the async and await keywords. That way it only returns after the userService.authenticate(..) call is fully processed.
Firebase QuerySnapshot has a docs property of type Array<QueryDocumentSnapshot<T>>. You can use that and the Array.find to search for the user. You should also await for bcrypt.compare while you search for user.
function authenticate({ username, password }) {
return users.then(async (querySnapshot) => {
const { usersDocs: docs } = querySnapshot;
const userDoc = usersDocs.find(doc => {
return doc.data().username === username;
});
if (userDoc) {
let user = doc.data();
const pwdCompareResult = await bcrypt.compare(password, user.password);
console.log(password);
console.log(user.password);
console.log(pwdCompareResult );
if (pwdCompareResult ) {
const token = jwt.sign({ sub: user.id }, config.secret);
const { password, ...userWithoutPassword } = user;
return {
...userWithoutPassword,
token
}
}
}
})
}
Please consider using Firebase Authentication instead
Your auth implementation is not reliable and it transfers sensitive data to every users device, like usersname and password hashes. Firebase has a very solid authentication system that you should be using.

Async Await | Can't have server wait for response from database before using authenification

So i'm having a problem where i'm attempting to have an async call to my database to check if the username and password of an account exist before then checking to see if they are valid.
I'm running into the problem that the server skips my database call and then proceeds to the check before the information from the database is grabbed.
The code:
class HandlerGenerator {
login (req, res) {
let username = req.body.username;
let password = req.body.password;
let checkUsername = "";
let checkPassword = "";
var lData = {
username: req.body.username,
password: req.body.password
};
// For the given username fetch user from DB
var db = req.db;
var getUser = async () => {
var result = await (
User.findOne(lData , function(err, userLogin){
if(err){
console.log(err);
return
}
console.log(userLogin);
console.log("------====----");
console.log(userLogin.username);
checkUsername = userLogin.username;
checkPassword = userLogin.password;
console.log("------====----");
console.log(checkUsername);
console.log(checkPassword);
})
);
console.log("--00--");
console.log('result' + result)
return result;
};
console.log("does this work?");
if (username && password) {
console.log("------==2==----");
console.log(checkUsername)
console.log(checkPassword)
if (username === checkUsername && password === checkPassword) {
let token = jwt.sign({username: username},
config.secret,
{ expiresIn: '24h' // expires in 24 hours
}
);
// return the JWT token for the future API calls
res.json({
success: true,
message: 'Authentication successful!',
token: token
});
} else {
res.send(403).json({
success: false,
message: 'Incorrect username or password'
});
}
} else {
res.send(400).json({
success: false,
message: 'Authentication failed! Please check the request'
});
}
}
index (req, res) {
res.json({
success: true,
message: 'Index page'
});
}
}
When I run this the "Does this work?" Comment always runs first and i'm confused on what I am messing up
You have two main issues here.
First, async returns an AsyncFunction, which returns a Promise when called. This means that at this point, nothing in getUser has been executed. Not only you need to actually call getUser to start running what is inside it, but you also need to await the result, otherwise you have absolutely no guarantee that the execution will be over.
Instead of going through this trouble, making your login function async seems a more reasonable choice, as you are trying to do asynchronous operations inside it. You could then remove the getUser function and only keep the var result = await User.findOne(....) part.
The other issue, as multiple persons said in the comments, is that you need to await on a Promise.
Looking at your function call, it looks like your findOne functions uses a callback rather than a promise. Do check the documentation, some libraries support both and might indeed return a promise if you do not pass in any callback.
If it does, you should not pass any callback. The result of the "awaited" call should be userLogin.
Otherwise, wrapping the function so that it returns a promise is straightforward.
The basic pattern is as follows
// this is the function that we want to wrap
function funcToWrap(someParam, callback) {
// does stuff
callback(null, "some result");
}
// this is the wrapper
function wrappedFunction(someParam) {
return new Promise((resolve, reject) => {
funcToWrap(someParam, (err, res) => {
if (err === null) {
reject(err);
} else {
resolve(res);
}
});
});
This transforms your callback-based function into a promise-based one. You can then of course await on wrappedFunc and use it as any other promise.
This is such a common pattern that a lot of libraries already implement this functionality. For example, the Promise library Bluebird provides a promisify function which does exactly this.
http://bluebirdjs.com/docs/api/promise.promisify.html
So instead of writing all of this yourself, you could simply write
var wrappedFunction = Promise.promisify(funcToWrap);

Why does this `Promise.all()` statement not wait for the promises to resolve?

I wrote a piece of code inside an async function that shall create four users concurrently, save them to a MongoDB and print a message when this is done:
// Concurrently create dummy users
const dummyUserDict = [
"name1",
"name2",
"name3",
"name4"
];
const dummyUserPromises = dummyUserDict.map(async (name) => {
let user = new User({
_id: new mongoose.Types.ObjectId,
username: name
});
return user
.save()
.then((result) => {
console.log('Created user ' + result.username + '!');
});
});
try {
await Promise.all[dummyUserPromises];
} catch(e) {
console.log(e);
}
console.log('Stop here!');
I'd expect all dummyUserPromises to be resolved when reaching the end of the code as I explicitly await them with Promise.all beforehand. When I turn on my debugger and set a breakpoint at console.log('Stop here!') I find that they are all still pending:
Why is this?
You have to call the function:
await Promise.all(dummyUserPromises)
Using [] you try to access a property of the Promise.all function, which will result in undefined and await undefined won't wait for anything.

How to do error handling with async/await without having to await

Take the following contrived example:
const housekeepingStuff = async function (data) {
const result = await notImportant(data);
result.more = 'yawn';
storeInDatabase(result);
};
const getStuff = async function () {
try {
const data = await getData();
data.extra = 'wow';
housekeepingStuff(data); // <---- don't want to await... but need to for error catching
return Promise.resolve(data);
} catch (err) {
return Promise.reject(err);
}
};
try {
const myData = await doSomeStuff();
res.send(myData);
} catch (err) {
console.log(err);
res.sendStatus(400);
}
I want to return the data from getStuff () ASAP without waiting for housekeepingStuff() but if I don't await that function then I have an uncaught error.
I could call housekeepingStuff() outside the getStuff() function, after getting and sending the data to whoever wants it:
try {
const myData = await doSomeStuff();
res.send(myData);
await housekeepingStuff(data); // <---- am awaiting but who cares because nothing follows
} catch (err) {
console.log(err);
res.sendStatus(400);
}
But that doesn't seem right because I don't want to have to remember to call housekeepingStuff() every time I call doSomeStuff()... it should ideally be handled "internally".
What is the correct approach here?
A promise (or async) function has 2 possible outcomes:
A successful outcome
An error outcome
To get either outcome, you must wait for it. You can't wait for 1 condition and not for the other, because the entire thing needs to execute so you can find out what the outcome was.
Otherwise you're really asking the javascript engine: Please predict for me if the function will fail, and if it does, await it.
The correct approach therefore is to just await it.
However, if you don't care about either successful or failed outcomes of this function, just call the function via another async function that eats all the errors:
async function doSomeStuffAndIgnoreError() {
try {
await doSomeStuff();
} catch (e) {
console.error(e);
}
}

Async post function in NodeJS

I am trying to query mssql database to get password for the user, that is sending post request and then just show it in console. But the promise I've made isn't working the way I wanted to and the password isn't loaded into the variable. Here's my code:
app.post('/test', function () {
let user = 'User';
let query = 'SELECT [Password] as password FROM [Table] where [User] = ' + SqlString.escape(user);
let password = (async function () {
try {
let pool = await sql.connect(dbConfig);
let result = await pool.request()
.query(querys);
console.dir(result.recordset[0].password) //Value here is OK
return result.recordset[0].password
} catch (err) {
// ... error checks
}
})()
console.log(password); //here I am getting "Promise { pending }"
});
The result I get is: Promise { pending }
'DatabasePassword'
Here is an example of using async / await with express.
Express doesn't handle the errors, but that's no big problem you can handle them yourself, you could even wrap into a generic error handler to make things easier.
Anyway here is your code modified to use async/await in express.
app.post('/test', async function (req, res) {
try {
const user = 'User';
const query = 'SELECT [Password] as password FROM [Table] where [User] = ' + SqlString.escape(user);
const pool = await sql.connect(dbConfig);
const result = await pool.request()
.query(querys);
const password = result.recordset[0].password;
console.log(password);
res.end(password);
} catch (e) {
res.end(e.message || e.toString());
}
});
Had the same problem while using the MongoDB database, just made the post request async by adding keyword async to the function:
app.post('/register', async function(req,res){
const myData = {username: req.body.name}
const doesUserExists = await UserModel.exists(myData)
})
Functions marked as async returns a promise. That is a wrapper around a future resolution (value or error). A function that returns a promise (which async functions does automatically) either has to be resolved as a promise by chaining .then(..) or by "unwrapping" it with await.
A function that somewhere in its code awaits an async function needs to be async itself. That means that whatever calls that function needs to await it or resolve it as a promise and so forth.

Categories