From one vuex action (togglePostLike) I'm trying to await another vuex action (requiresAuth) using async/await.
I had expected that if return Promise.resolve() was never called then the first action should get stopped at await dispatch (and get garbage collected), but it proceeds to the console.log('this is called SECOND') even without the resolve.
Shouldn't await stop the execution without resolve? How can I get that intended behavior?
/store/modules/post.js
async togglePostLike ({ dispatch, rootState }, post) {
await dispatch('user/requiresAuth', null, { root: true })
console.log('this is called SECOND, but why without resolve')
// I don't want this stuff to execute without the resolve
if (post.likes.includes(rootState.user._id)) {
dispatch('addPostUnlike', post)
} else {
dispatch('addPostLike', post)
}
},
/store/modules/user.js
async requiresAuth ({ state }) {
if (!state.authenticated) {
router.push({ name: 'login', query: { comeBack: true } })
$(`.modal`).modal('hide')
console.log('this is called first')
} else {
console.log('this is NOT called')
return Promise.resolve()
}
},
EDIT: how it looks without async/await
As I understand from the vuex docs, dispatch returns a promise anyway. Using the following:
post.js
if (dispatch('user/requiresAuth', null, { root: true })) {
// still executes before return false
}
user.js
requiresAuth ({ state }) {
if (!state.authenticated) {
// do stuff (does it)
return false
}
return true
}
An async function like requiresAuth always returns a promise that is resolved with the return value (including undefined if no return statement is evaluated). It does not make a difference whether the function ends without a return, with a return;, with a return undefined;, with a return Promise.resolve() or with a return Promise.resolve(undefined) - they are indistinguishable from the outside.
Either return a boolean and then branch on it, or just throw an exception.
Related
Hello I am a new coder and I saw this Electron helper to prompt for a value via input. https://github.com/p-sam/electron-prompt. I was wondering how I would store the value from user input. This is the code I have but I don't really understand how to pull out the data(user input) from the code to use. I would appreciate any help, thank you!
async function getStoreId()
{
prompt({
title: 'Get StoreId',
label: 'Store ID: ',
value: '',
inputAttrs: {
type: 'guid'
},
type: 'input'
})
.then((r) => {
if(r === null) {
console.log('user cancelled');
} else {
console.log('result', r);
//storeid = r;
}
})
.catch(console.error);
}
let storeid = await getStoreId;
console.log(storeid);
The prompt() function in the electron-prompt library returns a promise.
A promise can only be in one of three states:
Pending
Fulfilled, or
Rejected
Quite often, due to the sequential nature of Javascript, if you try and console.log() a promise before it has been fulfilled, you will receive a Promise { <pending> } message.
As you have wrapped your own function getStoreId() around the prompt() function (which returns a promise), you will not return anything unless you place a return statement in front of the prompt() function.
As a result, your getStoreId() function will now return the promise.
To handle the returned promise you must follow it up with a .then() method. The .then() method will then be processed when the promise is resolved (IE: It is no longer in the 'pending' state), meaning it has either been fulfilled or 'rejected'.
Inside the .then() method you can extract the result and either process it then and there or pass it on to another functions of yours for clearer, cleaner, more easily readable code.
function getStoreId() { // Removed async keyword
return prompt({ // Added return statement
title: 'Get StoreId',
label: 'Store ID:',
value: '',
inputAttrs: {type: 'guid'},
type: 'input'
})
.then((result) => {
if (result === null) {
console.log('user cancelled');
} else {
return result; // Return the result
}
})
// PS: Don't forget to handle any caught errors gracefully.
.catch(console.error);
}
// Called only when your getStoreId() function has resolved.
function useResult(result) {
// Do something useful with your result.
console.log(result);
}
// Calling "getStoreId()" without a ".then()" method will only
// return a "Promise { <pending> }" message at this point in the code.
// console.log(getStoreId());
// Let's show the prompt window.
getStoreId()
.then((result) => { useResult(result); })
I am testing a Vue component's method that performs a basic fetch, and updates a data value in the .finally() block of that fetch. I can confirm that my test reaches that .finally() block, but the data value is never updated.
My method is:
updateProfile () {
fetch(updateProfileEndPoint, {
method: 'POST',
body: {email: test#test.com, id: 1234, name: 'bob},
})
.catch((error) => {
this.errorField = true;
})
.finally(() => {
this.profileUpdated = true;
});
In my Jest test, I have:
const wrapper = mount(ProfileComponent, { store,
data () {
return {
profileUpdated: false,
};
},
});
global.fetch = jest.fn(() =>
Promise.resolve({
profileUpdate: 'complete',
})
);
wrapper.vm.updateProfile();
expect(wrapper.vm.profileUpdated).toBe(true);
However, profileUpdated remains false. Strangely, if I console.log(this.profileUpdate) in that method, the updated value true does log. However, my tests still receive false.
Your assertion occurs before the asynchronous fetch call actually completes.
One solution is to return the fetch result (a Promise) from updateProfile(), allowing the test to await the call:
// MyComponent.vue
export default {
methods: {
updateProfile() {
return fetch(...).catch(...).finally(...)
} 👆
}
}
// MyComponent.spec.js 👇
it('updateProfile() sets profileUpdated flag', async () => {
const wrapper = mount(...)
👇
await wrapper.vm.updateProfile()
expect(wrapper.vm.profileUpdated).toBe(true)
})
So i've been trying to make a 'log in' for my vue app. User logs in by clicking on the button and running the following method:
async signIn() {
this.getfireBaseData(await this.authenticate());
},
Here are both of the methods used inside the previous one:
async authenticate() {
auth
.signInWithPopup(provider)
.then((result) =>
this.$store.commit("setUserData", {
email: result.additionalUserInfo.profile.email,
picture: result.additionalUserInfo.profile.picture,
name: result.additionalUserInfo.profile.name,
})
)
.catch((err) => console.log(err.message));
return this.$store.state.userData;
},
async getfireBaseData(x) {
db.collection("rooms")
.get()
.then((snapshot) => {
this.firebaseData = snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}));
console.log(x);
if (x) {
this.$router.push(`/rooms/${this.firebaseData[0].id}`);
}
});
},
And the store:
state: {
userData: null,
},
mutations: {
setUserData(state, payload) {
state.userData = payload;
},
}
I'm expecting it to run getfireBaseData as soon as the authenticate() resolves its promise. For some reason - when i'm running the code for the first time - authenticate() leaves me with fullfiled promise with the value of "null" - the value in the store. It seems like authenticate() does modify the store value but does not return it as i run it instantly. Any solves to that issue?
That's because you are returning directly your store data from authenticate(), not the promise chain.
So you are not waiting your auth.signInWithPopup but resolve directly the current value of your store.
try to returning directly auth.signInWithPopup(provider) and return your userData from the resolve callback or via an other "then" after your catch.
But it might not be a good idea to mix async/await with .then.catch.
It's error prone.
I call a function then output it's return value in console, like so:
Index.getInitialProps = async ctx => {
const { loggedInUser } = await checkLoggedIn(ctx)
console.log('Data actually returned:')
console.log(loggedInUser)
return { loggedInUser }
}
Pretty straight forward...
The checkLoggedIn function is as follows:
export function checkLoggedIn(ctx) {
ctx.apolloClient.query({
query: GET_ME,
})
.then(({ data }) => {
console.log('Data to be returned:')
console.log(data)
return { loggedInUser: data }
})
.catch(() => { return { loggedInUser: {} } })
}
Again, pretty straight forward. This is essentially an exact copy of this example: checkLoggedIn() and getInitialProps
Now what I would expect to happen is that I should see the following in the console:
Data to be returned:
{ <DATA> }
Data actually returned:
{ loggedInUser: { <DATA> }
Instead, I see:
Data actually returned:
undefined
Data to be returned:
{ <DATA> }
Which makes zero sense as the function is returning the correct data, and it should await for this returned data before proceeding to the console.log.
Instead, it completely ignores the 'await' and doesn't even bother waiting for the function to return a value before continuing.
What is going on here?
() => { return { loggedInUser: {} } } is a function that returns { loggedInUser: {} }. Ditto for the function inside then.
Thus, checkLoggedIn has no return. It should return the promise it constructs:
return ctx.apolloClient.query({
You can only (usefully) await a promise.
The function checkLoggedIn has no return statement, so it returns undefined.
undefined is not a promise, so awaiting it has no effect.
Change checkLoggedIn so it returns the promise.
I think you should make checkLoggedIn an async function
I have a react-native app where I do some authentication.
I have the following code where I check if the token is not expired and its available.
export const isLogged = () => {
AsyncStorage.getItem('#token')
.then( token => {
if (typeof token !== 'undefined') {
if (tokenExpired(token)) {
return false
}
return true
}
return false
} )
.catch( error => {
return false
} )
}
But in my code if I do:
let isUserLogged = isLogged()
console.log(isUserLogged) // -> returns undefined, but should return true because the token is there and its not expired.
Does anyone has some idea why its like this, I'm doing something wrong?
You are trying to synchronously get a result that only becomes available asynchronously.
Change your code like this:
Add return before this call:
AsyncStorage.getItem('#token')
This will make your isLogged function return something: a promise
Use this promise in your main code:
isLogged().then( isUserLogged => {
console.log(isUserLogged);
});
The fact that your function isLogged returns a promise (when you return it, that is), is an example of chaining.
Your isLogged function is an asynchronous function, that is - it operates on values that might not be available to you in the exact moment of function execution, but delayed in time.
Since you are already operating on Promises here, you could just return the result of your AsyncStorage promise chain, and then attach additional handlers when invoking isLogged() function like this:
// inside your isLogged() function
return AsyncStorage.getItem('#token')
.then(...)
... rest of your code unchanged ...
// when invoking isLogged()
isLogged().then((isLogged) => {
console.log("is user logged: ", isLogged);
});
You should also read more about Promises in JavaScript.