Reputation: 413
I've recently discovered the promises, and they made my life much easier. But there is a specific case that I'm not being capable to handle. It is when I have to call a promise inside then().
Here is my code:
const firebaseAuth = require("firebase/auth");
const auth = firebaseAuth.getAuth();
const { User } = require('../models/User');
app.post('/create_user', (req, res) => {
user_uid = req.body.params.uid;
newUserEmail = req.body.params.email;
newUserPassword = req.body.params.password;
let user;
firebaseAuth.createUserWithEmailAndPassword(auth, newUserEmail, newUserPassword)
.then((userCredential) => new Promise((resolve, reject) => {
if(userCredential == undefined) throw Error("createUserWithEmailAndPassword failed");
user = new User(userCredential.user.uid, req.body.params.userAttributes);
resolve()
}))
.then(firebaseAuth.sendPasswordResetEmail(auth, user.attr.email, null))
.then(/*other functions using User object*/)
.then(() => { // finished promise chaining
res.status(200).send();
})
.catch((e) => {
console.log(e)
res.status(403).send();
})
});
The problem is, the .then(firebaseAuth.sendPasswordResetEmail(auth, user.attr.email, null))
is being called before the user is initialized in user = new User(userCredential.user.uid, req.body.params.userAttributes);
.
Could someone please help me understand why is this happening? And in case I have to call a promise inside a then(), Do I also have to nest a .catch()? Or my single .catch() at the end of my function will be capable to handle possible errors?
Edit: User does some async tasks inside the constructor, because it has to handle the images.
And I can only Initialize it, after firebaseAuth.createUserWithEmailAndPassword(auth, newUserEmail, newUserPassword)
because I have to get the generated Id in userCredential
Upvotes: 1
Views: 613
Reputation: 62676
We should never find new Promise()
wrapping a library that already creates promises.
Also remember that the form of a chain is....
// promiseA, promiseB & promiseC are expressions that evaluate to promises
return promiseA.then(resolutionOfPromiseA => {
// synchronous work
return promiseB;
}).then(resolutionOfPromiseB => {
// synchronous work
return promiseC;
}).catch(error => {})
In newer syntax:
async function myFunction() {
try {
let resolutionOfPromiseA = await promiseA;
// synchronous work
let resolutionOfPromiseB = await promiseB;
// synchronous work
return promiseC;
} catch(error) {
}
Sticking with the OP's older syntactic style (which is perfectly good as long as it remains consistent)
let user;
firebaseAuth.createUserWithEmailAndPassword(auth, newUserEmail, newUserPassword)
.then(userCredential => {
// note that we don't create a promise
if(userCredential == undefined) throw Error("createUserWithEmailAndPassword failed");
user = new User(userCredential.user.uid, req.body.params.userAttributes);
// note that we return this one. It's good form, even if the next then doesn't use the result
return firebaseAuth.sendPasswordResetEmail(auth, user.attr.email, null)
})
.then(res => { // this res is the result of firebaseAuth.sendPasswordResetEmail
// user is in scope here because it's in the containing scope
/* other functions using User object */
})
.then(() => { // finished promise chaining
res.status(200).send();
})
.catch((e) => {
console.log(e)
res.status(403).send();
})
edit If you really wanted user
in the block after sendPasswordReset
, and you really didn't want to keep a user variable in the containing scope, you could say...
// move let declaration here
let user = new User(//...
// string an extra return user here
// some linters think this is a faux pas
return firebaseAuth.sendPasswordResetEmail(auth, user.attr.email, null)
.then(() => return user)
)}.then(user =>
Upvotes: 1
Reputation: 638
You can use async
function and try
block to await
the userCredential value like this:
app.post('/create_user', async(req, res) => {
user_uid = req.body.params.uid;
newUserEmail = req.body.params.email;
newUserPassword = req.body.params.password;
let user;
try {
const userCredential =
await firebaseAuth.createUserWithEmailAndPassword(
auth,
newUserEmail,
newUserPassword
);
if (userCredential == undefined)
return res
.status(400)
.send('❌ CreateUserWithEmailAndPassword failed 🙁');
user = new User(
userCredential.user.uid,
req.body.params.userAttributes
);
await firebaseAuth.sendPasswordResetEmail(auth, user.attr.email, null);
return res.status(201).send('Created!! 😀');
} catch (error) {
console.log('❌ Error:', error);
return res.status(400).json({
message: error,
});
}
});
Upvotes: 1
Reputation: 707
I see two issues here:
.then(firebaseAuth.sendPasswordResetEmail(auth, user.attr.email, null))
to
.then(() => firebaseAuth.sendPasswordResetEmail(auth, user.attr.email, null))
notice the the the arrow function wrapping the call to firebaseAuth
, before your code was the equivalent of asking firebaseAuth
to return a function that would handle that step of the promise chain.
return new Promise()
is to do the validation and get the user, you can simply. .then((userCredential) => {
if(userCredential == undefined) throw Error("createUserWithEmailAndPassword failed");
return new User(userCredential.user.uid, req.body.params.userAttributes);
})
and the user will be available in the next chain as
.then(user => {
// do stuff with user
})
Upvotes: 1