Async method not waiting for a function - VUE - javascript

i'm having this error and haven't got to resolve it though have researched a lot in MDN and here. As title saysinto VUE i'm trying to use async and await but js is not waiting the 'await' function to end. Here it is:
methods: {
async search (terms, done) {
console.log('1.')
this.filter = this.$refs.chipsInput.input
await this.loadtags()
console.log('3.')
done(this.tagsList)
},
loadtags () {
this.$axios
.get('/api/tags/?id__icontains=&id=&name__icontains=' + this.filter + '&name=&ordering=name&page_size=20')
.then(response => {
console.log('2.', response.data.results)
let temp = response.data.results
this.tagsList = temp.map(obj => {
return {
name: obj.name,
label: obj.name,
value: obj.name,
idField: obj.id
}
})
})
},
I am not able to post pictures yet, but add a link where you can look the console log where js prints the '3.' (which is placed after the await call) before '2.':
Image:
console
¿What am i doing wrong? already tried modifying the await like this:
let foo = await this.loadtags() and including a 'return 0' at the end of loadtags function but didn't work for me. Probably is a dumb thing, excuse me for that.

You aren't returning anything from the loadtags method, so the code doesn't wait.
Change this:
loadtags () {
this.$axios
.get(...
To this:
loadtags () {
return this.$axios
.get(...
async/await is more or less just sugar over Promises, so returning the Promise gives you something to await in the other method.

This is how I resolved this in my Vue application.
Before a user submits a new "tag" with submitNewTag() I need to check if it exists already in the list of tags, using async theTagExists().
submitNewTag() {
this.clearError();
this.theTagExists().then((res) => {
if (!res) {
console.log("TAG DOES NOT EXIST, SO ADD IT TO THE DATABASE");
this.saveTagToDatabase();
}
});
},
async theTagExists() {
console.log("CHECKING IF A TAG EXISTS");
await axios.get(`${this.apiUrl}/alltags`).then((res) => {
console.log("CHECKING IS DONE");
this.tagExists = res.data.allTags.some(
res =>
res.name.trim().toLowerCase() ===
this.newTag.tagName.trim().toLowerCase()
);
});
console.log("RETURN THE RESULT");
return this.tagExists;
},

Related

Cannot read property 'code' of undefined from promise, context api (react hook)

I am making my first hybrid web app using react hook.
I am faced with a timing problem with promise and context api.
Here are the logic I am facing with.
A function fetchApplications gets data from firebase firestore, it is defined in globalcontext.js (called in tableView)
tableView.js calls fetchApplications in useEffect.
fetchApplications is defined as promise function, I expect it will resolve(return) data until data fetched, it will resolove(return) object like {code:200, data:data}
problem
in the fetchData.code,
Cannot read property 'code' of undefined
Here is my code
In the tableview.js
React.useEffect(() => {
context.fetchApplications(auth.userId, "")
.then(function (fetchData) {
console.log("Fetched data: ", fetchData); ///this is undefined
if (fetchData.code !== 200) { /// error part
alert(fetchData.msg);
}
if (!fetchData.data) {
alert("No applications");
}
setsData(fetchData.data);
});
}, []);
In the GlobalContext.js
async function fetchApplications(userId, role) {
return new Promise((resolve, reject) => {
// resolve({ code: 200, data: "data" }); //If I add this code, it will be alright.
let dataArray = [];
let applicationRef = db
.collection("Users")
.doc(userId)
.collection("Applications");
applicationRef
.get()
.then(function (qs) {
qs.forEach(function (doc) { //doesn't work
console.log(doc.id, " => ", doc.data());
console.log(doc.size, " => ", typeof doc);
dataArray.push(doc.data());
});
return Promise.resolve(dataArray);
})
.then((data) => {
console.log("Global, Fetched Data", dataArray);
if (data) {
resolve({ code: 200, data: data });
}
return;
});
}).catch(function (error) {
reject({ code: 400, msg: "시스템에러 고객센터에 문의해주세요" });
});
}
wrote in codesendbox
If I was write the wrong way of promise, please let me know.
thanks
You're implementing a couple of bad practices and have some major issues. For starters, fetchApplications is marked as async but you're returning a manually created promise which is quite overkill as your fetching code actually generates a promise which you should directly return. Furthermore:
.then(function (qs) {
qs.forEach(function (doc) { //doesn't work
console.log(doc.id, " => ", doc.data());
console.log(doc.size, " => ", typeof doc);
dataArray.push(doc.data());
});
return Promise.resolve(dataArray);
})
I am not sure what exactly "//doesn't work" should mean but return Promise.resolve(dataArray); won't cut it for you. You're already in the then chain, so you can't resolve anything from the main promise at this point. You should just pass the data to the next then callback as return dataArray;.
All in all, I will suggest ditching the then-ables syntax all together and migrate to async/await altogether:
async function fetchApplications(userId, role) {
try {
const dataArray = [];
const applicationRef = db
.collection("Users")
.doc(userId)
.collection("Applications");
const querySnapshot = await applicationRef.get();
querySnapshot.forEach(doc => {
dataArray.push(doc.data());
});
return {
code: 200,
data: dataArray
};
}
catch (error) {
return {
code: 400,
msg: '시스템에러 고객센터에 문의해주세요'
};
}
}
Then, in your react component/hook:
React.useEffect(() => {
const fetchApplicationDataAsync = async () => {
const result = await context.fetchApplications(auth.userId, "");
if (result.code !== 200) {
alert(result.msg);
}
if (!result.data) {
alert("No applications");
}
setsData(result.data);
}
fetchApplicationDataAsync();
}, [auth.userId]);
Another problem and bad practice is that you're not specifying your dependencies for the useEffect hook. You have 2 external dependencies: the auth.userId paramater and the ontext.fetchApplications function. We alleviate one of the problem by creating the fetch function in the body of useEffect itself. However, the auth.userId should go into the dependency array as stated above.
You have to check for fetchData to be defined before accessing its properties.
A short form would be
if (fetchData && fetchData.code !== 200){...}
Applied to your code:
React.useEffect(() => {
context.fetchApplications(auth.userId, "")
.then(function (fetchData) {
console.log("Fetched data: ", fetchData); ///this is undefined
if (fetchData && fetchData.code !== 200) { /// error part
alert(fetchData.msg);
}else {
alert("No applications");
}
setsData(fetchData.data);
});
}, []);
By calling then() on the fetchApplications() function, as follows, you pass to the callback the fullfilment value from the Promise returned by fetchApplications() (i.e. fetchData gets the value returned by fetchApplications()).
context.fetchApplications(auth.userId, "")
.then(function (fetchData) {...}
However, fetchApplications() returns a Promise that resolves with undefined because, actually, you don't return the Promises chain. This is why you get an error on fetchData.code.
Adapting fetchApplications() as follows (using await, since you use async) should do the trick (untested!):
async function fetchApplications(userId, role) {
try {
let dataArray = [];
let applicationRef = db
.collection('Users')
.doc(userId)
.collection('Applications');
const qs = await applicationRef.get();
qs.forEach(doc => {
console.log(doc.id, ' => ', doc.data());
console.log(doc.size, ' => ', typeof doc);
dataArray.push(doc.data());
});
return { code: 200, data: dataArray };
} catch (error) {
return { code: 400, msg: '시스템에러 고객센터에 문의해주세요' };
}
}
Note that in any case you return an object with a code property, so no more problem when doing fetchData.code.

Async/await with sync func changing react state

I get problems with async/await functions and changing state in React.
This is my async function, which is triggered by clicking on the button:
async startNewEntry() {
this.addIssue();
let issue_id;
console.log(this.state.timeEntry, "started_timeEntry")
if (this.state.timeEntry?.issue?.id) {
issue_id = this.state.timeEntry?.issue?.id;
} else {
issue_id = (await this.issueService.list()).data[0]?.id;
}
const { data } = await this.timeEntryService.create({
comments: this.state.timeEntry.comments,
issue_id,
spent_on: moment(new Date()).format("YYYY-MM-DD"),
hours: 0.01,
activity_id: this.localStorageService.get("defaultActivityId")
});
In this function I use this.addIssue, which use this.createIssue, which changing my class component state:
addIssue() {
this.projectService.list([]).then(response => {
response.data = response.data.filter((x: any) => x.status === 1);
this.setState(
{
activeProjects: response.data
},
() => {
this.createIssue();
}
);
});
}
createIssue() {
this.issueAddService
.create({
project_id: this.state.activeProjects[0].id,
tracker_id: trakerId,
priority_id: priorityId,
subject: this.state.timeEntry.comments,
description: this.state.timeEntry.comments
})
.then(response => {
let timeEntry = this.state.timeEntry;
timeEntry.issue = response.data;
this.setState({
timeEntry
});
})
.catch(error => {
console.log("error", error);
});
}
As you can see, in my async function I new to have my new State, but actually async function works before my this.addIssue function. I know that question might be little freaky, but Thanks in forward!!
I am not a React expert, but I don't fully understand why there are lot of setState invocations spread around the place.
If you leave the setState to the end of the function, then you might not need to worry about correctly sequencing asynchronous calls to it (although the other answer does show how this can be achieved).
Perhaps invoking it once might make the code clearer. I welcome corrections...
async startNewEntry() {
const activeProjects = await fetchActiveProjects()
const issue = await this.createIssue()
const timeEntry = await createTimeEntry({ issue, comments: this.state.timeEntry.comments })
this.setState({ activeProjects, issue, timeEntry })
}
async fetchActiveProjects() {
const { data } = await this.projectService.list([])
return data.filter(({ status }) => status === 1)
}
async createIssue() {
const { data } = await this.issueAddService.create({
project_id: this.state.activeProjects[0].id,
tracker_id: trakerId,
priority_id: priorityId,
subject: this.state.timeEntry.comments,
description: this.state.timeEntry.comments
})
return data
}
async createTimeEntry({issue, comments}) {
const { data } = await this.timeEntryService.create({
comments,
issue_id: issue?.id || (await this.issueService.list()).data[0]?.id,
spent_on: moment(new Date()).format("YYYY-MM-DD"),
hours: 0.01,
activity_id: this.localStorageService.get("defaultActivityId")
})
return data
}
You can probably speed this up further by parallelizing the first two async calls:
async startNewEntry() {
const [activeProjects, issue] =
await Promise.all([fetchActiveProjects(), this.createIssue()])
const timeEntry = await createTimeEntry({ issue, comments: this.state.timeEntry.comments })
this.setState({ activeProjects, issue, timeEntry })
}
If you want startNewEntry to wait to do its work until after addIssue has done its work, you need to:
Have addIssue return a promise it fulfills when it's finished its work, and
Use await when calling it: await this.addIssue();
If you need startNewEntry to see the updated state, addIssue's promise will need to be fulfilled from the state completion handler callback, like this:
addIssue() {
// *** Return the promise chain to the caller
return this.projectService.list([]).then(response => {
response.data = response.data.filter((x: any) => x.status === 1);
// *** Create a new promise
return new Promise(resolve => {
this.setState(
{
activeProjects: response.data
},
() => {
this.createIssue();
resolve(); // *** Fulfill the promise
}
);
});
});
}
Often, new Promise is an anti-pattern, particularly when you have another promise you can chain from. But in this case, since you need to wait for the callback from setState (which isn't promise-enabled), it's appropriate. (
Note my comment on the question. I think you're setting up an endless loop...

Vuex action not waiting to finish axios promise

I encounter a strange situation developing an application in Laravel + VueJS/Vuex stack.
I understand that if a promise is not returned the parent function calling it will not wait for it to resolve so things will go asynchronous. Axios returns a promise by default when calling a resourse through http.
So i have the parent function which looks like this:
fetchInvoiceSeries() {
var arr = []
let invsrs = this.$store.getters['getInvoiceSeries']
if (invsrs == null) {
return this.$store
.dispatch('get_invoice_series')
.then(() => {
invsrs = this.$store.getters['getInvoiceSeries']
if (invsrs != null) {
invsrs.forEach(function(s) {
arr.push({
value: s.id,
text: s.series + ' / ' + s.increment
})
})
this.series = arr
} else {
console.log('Error while fetching invoice series!')
}
})
.catch(e => {
console.log(e)
})
} else {
invsrs.forEach(function(s) {
arr.push({
value: s.id,
text: s.series + ' / ' + s.increment
})
})
this.series = arr
}
}
And here is the function defined in action part of the vuex module:
get_invoice_series({ commit }) {
return get('/api/series/0')
.then(response => {
if (response.data && typeof response.data !== undefined) {
let payload = response.data
commit('SET_INVOICE_SERIES', payload)
} else {
console.log('error', error)
}
})
.catch(error => {
console.log('error', error)
})
},
So as you can see i am returning the get request from axios inside the action. In the parent i am calling the action and the "then" keyword in order to do some processing after the action it's done. Also i am using arrow function because i need the context in the parent function in order to call this.$store ...
The problem is that even after checking the getter to see if the state have the invoice series and getting them using the get_invoice_series action i still don't have the invoice series in memory judging by the code i wrote. The console keeps loggin 'Error while fetching invoice series!' the first time i execute the code and the second time (after the information exists in state), the code skips fetching the invoice series (as expected).
Can you tell me what i am doing wrong ? Thank you!
Your error comes from invsrs being null the first time, and not null the second time.
This means that your function get_invoice_series({ commit }) is asynchronous, and that it returns a promise.
For more readability, maybe you should make your call independently from your return statement, using async/await expressions :
async get_invoice_series({ commit }) {
const response = await get('/api/series/0')
if (response.data === undefined) return null
const payload = response.data
commit('SET_INVOICE_SERIES', payload)
return payload
},
And then make your calls wait for this fetch to process :
async fetchInvoiceSeries() {
let arr = []
const invsrs = await this.$store.getters['getInvoiceSeries']
// ...
It's pure conjecture here, let me know if it helps or not.

Async function returning undefined in NodeJS

So, I'm new trying to understand how async functions work. Doing it with "Resolve, Reject" from Promises, works fine. But when I try to apply it with async instead of new Promise, for some reason, the function returns undefined. Here is my code :)
note: Sorry for my english, not fluent speaker :)
category = body.category
obtenerCategoria = async(category) => {
Categoria.findOne({ descripcion: category })
.exec((err, categoriaDB) => {
if (err) {
throw new Error(err)
}
if (!categoriaDB) {
return res.status(400).json({
ok: false,
msg: 'Categoria no encontrada'
})
}
console.log(categoriaDB); // Works fine here
return categoriaDB
})
}
crearClase = async() => {
categoria = await obtenerCategoria(category);
console.log(categoria); //getting nothing here
}
crearClase()
.then()
.catch(e => {
return e
})
You don't need to use callback function when you use async/await
Try this code:
obtenerCategoria = async(category) => {
const categoriaDB = await Categoria.findOne({ descripcion: category });
if (!categoriaDB) {
return res.status(400).json({
ok: false,
msg: 'Categoria no encontrada'
})
}
console.log(categoriaDB); // Works fine here
return categoriaDB
}
obtenerCategoria doesn't have a return value. Additionally, you can use await on the Categoria.findOne() call and use try / catch instead of the call to exec(), something like this example.

Redis Javascript Async Function

I have an array of Id' and i need to get the details for each of them.
i currently have this.
const redis = require('redis');
const redisClient = redis.createClient(process.env.REDIS_PORT, process.env.REDIS_HOST);
const arrayList = [
{ id: 3444 },
{ id: 3555 },
{ id: 543666 },
{ id: 12333 },
];
async function getDetails(element) {
await redisClient.hgetall(element.id, (err, user) => {
if (err) {
console.log('Something went wrong');
// Handle Error
return err;
}
console.log('Done for User');
return user;
});
}
arrayList.forEach((element) => {
console.log('element');
await getDetails(element).then((res) => {
// Do Something with response for each element
});
});
This is the response i get right now. its not async. What am i doing wrong please.
element
element
element
element
Done for User
Done for User
Done for User
Done for User
So how things go on in async/await is, you create an async function and inside that function you await for other operations to finish. You call that async function without await OR you wrap it(func call) inside another async function.
arrayList.forEach((element) => {
console.log('element');
let returnedPromise= getDetails(element);
console.log("Promise after getDetails function", returnedPromise);
});
This code change should resolve the error.
Array.forEach() does not wait for promises to execute before moving to the next item.
You could instead use a for-loop in an async function, like so:
async function main() {
for (const element of arrayList) {
const response = await getDetails(element);
// do something with reponse for each element
}
}
main()
.then(() => /* on success */)
.catch((err) => /* on error */);

Categories

Resources