when having issues with firebase promise chains resolving before the entire chain is completed. there is a login and signup modal that displays errors to the user when necessary. I building this with VUEJs
If the error is something like bad email formatting the promise chain works perfectly and displays the correct error message to the user. When the formatting is correct and the request is actually sent to fire base, the .then will fire before the initial firebase promise is fully resolved causing my if in the signup method statement to always resolve to false, and redirecting to /dashboard before the error gets back to the app from firebase.
there is poked at this thing for a few hours now. there is tried reorganizing the promise chain. there is a login in component that is having the exact same behavior.
Here is my method that fires when the signup button is clicked.
computed:{
error () {
return this.$store.getters.error
}
},
methods: {
signup () {
const userProfile = {
userName: this.userName,
firstName: this.firstName,
lastName: this.lastName,
password: this.password,
birthDate: this.birthDate,
address: this.address,
city: this.city,
state: this.state,
zip: this.zip,
email: this.email
}
if (this.email && this.password && this.userName) {
this.$store.dispatch(‘signUserUp’, userProfile)
.then( () => {
if (this.error) {
console.log(this.error)
return this.feedback = this.error.message
} else {
return this.$router.replace(‘/dashboard’)
}
})
} else {
return this.feedback = ‘Please enter all required fields’
}
}
}
This is action in my Vuex store the method dispatches to
signUserUp({commit}, payload) {
commit(‘setLoading’, true)
commit(‘clearError’)
let slug = null
let newUser = {}
let newError = {
test: ‘test’,
message: ‘User Name is already taken, please chose another.’
}
slug = slugify(payload.userName, {
replacement: ‘-‘,
remove: /[$*_=~.()’’!\-:@]/g,
lower: true
})
firebase.database().ref(‘users/‘ + slug).once(‘value’, snapshot => {
if (snapshot.exists()){
console.log(‘name exists’)
commit(‘setError’, newError)
} else {
console.log(‘user does not exist’)
firebase.auth().createUserWithEmailAndPassword(payload.email, payload.password)
.then(
cred => {
newUser = {
userId: cred.user.uid,
userName: slug,
firstName: payload.firstName,
lastName: payload.firstName,
birthDate: payload.birthDate,
adress: payload.adress,
city: payload.city,
state: payload.state,
zip: payload.zip,
email: payload.email
}
console.log(newUser)
commit(‘setUser’, newUser)
console.log(‘sign up complete’)
firebase.database().ref(‘/users/‘ + slug).set(newUser)
.then( () => {
commit(‘setLoading’, false)
console.log(‘user profile uploaded’)
})
.catch( error => {
console.log(error)
})
}
)
.catch(
error => {
commit(‘setLoading’, false)
commit(‘setError’, error)
console.log(error)
}
)
}
})
}
What when trying to do is check against the database to make sure the username is not already taken. If it has been taken to show the user the the error and stay at the signup form. If not, it to create the new account, upload the profile to my real time database, and then redirect to /dashboard if everything is successful.
This is my first do it 100% myself app, so please be gentle lol.
Thanks for the help!
Solution :
As explained in the documentation, signUserUp should return a promise, in order for to chain it with this.$store.dispatch(‘signUserUp’, userProfile).then(…). Firebase supports promises where possible. It’s possible to chain once() and flatten promise chain with async..await:
async function signUserUp({commit}, payload) {
try {
…
const snapshot = await firebase.database().ref(‘users/‘ + slug).once(‘value’);
…
const cred = await firebase.auth().createUserWithEmailAndPassword(payload.email, payload.password);
…
} catch (error) {
commit(‘setLoading’, false)
commit(‘setError’, error)
}
}
Solution 2:
If you need to wait on the asynchronous code in the action to complete then the action should return a Promise.
Probably the simplest way* to make this work with the current code is to wrap it in a new Promise.
signUserUp({commit}, payload) {
return new Promise(function(resolve, reject) {
/* existing function body */
});
}
Then add calls to resolve() when the asynchronous code has completed. (For example, at this point: console.log(‘user profile uploaded’) or after one of the errors.) Once you call the resolve callback it will invoke the then handler in the signup method. You can reject the Promise as well using the reject callback. In the case however it looks like you want the action to handle the error itself and not propagate it.
*Since it looks like the firebase API uses promises you could also attempt to return the result of the firebase functions.