Execute a function after a response of previous in Angular - javascript

I'm trying to execute a function after a success response of previous function has returned. Have been trying, with different approaches, but still in vain.
I want my service to post newLoan only after the the loanTerms have been added (which is a API call), but doesn't waits for that an execute the next function without the response of previous.
Before posting this question, I already tried different methods, Even though I'm putting my code inside the subscribe method of function. but still I doesn't performs the way I want it.
The problem is that I've list of products, and I've to perform a network operation on each product and then execute my other function, but it only waits for just 1st product, after that I doesn't wait and executes the next function.
Here is my code
{
this.newLoanForm.value.invoiceDate = this.loanDate.format();
document.getElementById('submitButton').style.display = 'none';
// Adding number of months against give loan term ID
let loanProducts = this.loanProductForm.value.products;
let loanTerm;
loanProducts.forEach(product => {
this.loanTermService.getLoanTerm(product.loanTermId).subscribe((response: any) => {
// console.log('Number of months: ', response.numberOfMonths)
loanTerm = response.numberOfMonths;
product.installmentStartDate = this.installmentStartDate.format();
product.monthlyInstallment = product.total / loanTerm;
// I want this function to executed after all the products have been completed their network activity, but it only waits for just 1st product, after that it executes the below code. how do I make it wait for all products.
this.loanService.postLoan(this.newLoanForm.value).subscribe((response: any) => {
console.log('Loan added successfully: ', response);
PNotify.success({
title: 'Loan added Successfully',
text: 'Redirecting to list page',
minHeight: '75px'
})
document.getElementById('submitButton').style.display = 'initial';
this.router.navigate(['searchLoan']);
}, (error) => {
console.log('Error occured while adding loan: ', error);
PNotify.error({
title: 'Error occured while adding loan',
text: 'Failed to add new loan',
minHeight: '75px'
})
document.getElementById('submitButton').style.display = 'initial';
})
}, error => {
console.log('Error while retrieving loanTermId: ', error);
});
});
this.newLoanForm.value.loanProducts = loanProducts;
console.log('Loan Products: ', this.loanProductForm.value);
here's how I tried the above code with promise and async and await
async calculateInstallments() {
// Adding number of months against give loan term ID
this.loanProducts = this.loanProductForm.value.products;
// let loanTerm;
this.loanProducts.forEach(async product => {
console.log('Call to get loanTerms: ', await this.loanTermService.getLoanTermById(product.loanTermId));
let response: any = await this.loanTermService.getLoanTermById(product.loanTermId);
await this.loanProductService.getLoanProductByLoanId(product.loanTermId).then(() => {
let loanTerm = response.numberOfMonths;
console.log('loanTerms', loanTerm);
product.installmentStartDate = this.installmentStartDate.format();
product.monthlyInstallment = product.total / loanTerm;
});
});
}
// putting the function I want to execute after the response of previous in the `then` method
await this.calculateInstallments().then(() => {
this.newLoanForm.value.loanProducts = this.loanProducts;
// Posting loan after the response of loanTerms Service
this.loanService.postLoan(this.newLoanForm.value).subscribe((response: any) => {
console.log('Loan added successfully: ', response);
PNotify.success({
title: 'Loan added Successfully',
text: 'Redirecting to list page',
minHeight: '75px'
});
document.getElementById('submitButton').style.display = 'initial';
this.router.navigate(['searchLoan']);
}, (error) => {
console.log('Error occured while adding loan: ', error);
PNotify.error({
title: 'Error occured while adding loan',
text: 'Failed to add new loan',
minHeight: '75px'
});
document.getElementById('submitButton').style.display = 'initial';
});
});
but it didn't work unfortunately.

I just answered a question today, about almost the same problem. Maybe you still need a solution, otherwise see it as another way to go.
Doesn't mind if you use async await or daisy chain style with new Promise. Since version 3.x of async framework, you'll be able to use the amazing iteration functions (don't know if all) as promise, if you don't use callback.
This is an simple example, how you could use the eachOf function for asynchronously tasks.
const async = require('async');
let items = [
{ firstName: 'John', lastName: 'Doe' },
{ firstName: 'Jane', lastName: 'Doe' },
{ firstName: 'Me', lastName: 'Myself And I' }
];
async.eachOf(items, (item, index, callback) => {
//here you could query db with vaulues from items array as item
console.log('this is item:', item);
new Promise(resolve => {
setTimeout(() => {
resolve(true);
}, 500);
})
.then(result => {
//maybe you need to do something else
console.log('this is the result:', result);
callback();
});
})
.then(() => {
//working ahead with daisy chain
console.log('All items updated');
});
I hope you can work with this setup or it's an inspiration to restructure this and using async await in another handy way.

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.

Unable to run a loop to update Object Array in SQLite with React Native

So this has been troubling me for a while, I have an array of objects that I want to insert into my SQLite DB. Each of the objects have 5 parameters and I have the SQL Query in place to run it. I was using a loop to iterate through the array and populate each of the objects via db transactions to SQLite. However, the db tasks are asynchronous which leads to the loop being completed before the task is run and incorrect data being populated into the db. The while loop in the code below doesn't work and I have tried the same thing with a for loop to no avail.
var i=0;
while(i<rawData.length){
console.log(rawData[i],i)
db.transaction(function (tx) {
console.log(rawData,i," YAY")
tx.executeSql(
'Update all_passwords SET title=?,userID=?,password=?,notes=?,category=? WHERE ID =? ',
[rawData[i].title,rawData[i].userID,rawData[i].password,rawData[i].notes,rawData[i].category,rawData[i].id],
(tx, results) => {
console.log("saved all data")
tx.executeSql(
"SELECT * FROM all_passwords ORDER BY id desc",
[],
function (tx, res) {
i++
console.log("Print Out Correct Data")
for(var i=0;i<res.rows.length;i++){
console.log(res.rows.item(i), i )
}
});
}
);
console.log("EXIT")
}
,
(error) => {
console.log(error);
}
);
}
I'm not familiar using async tasks with hooks but I believe that might be a potential solution. My intention is to populate the rawaData array of objects into the SQLDb in one go while I use a state to maintain the loading screen.
I did refer the below sources but wasn't able to come up with anything concrete.
react native insertion of array values using react-native-sqlite-storage
https://medium.com/javascript-in-plain-english/how-to-use-async-function-in-react-hook-useeffect-typescript-js-6204a788a435
Thanks in advance!
I made a little write up for you on how I would solve it. Read the comments in the code. If anything is unclear feel free to ask!
const rawData = [
{ title: "title", userID: "userID", password: "password", notes: "notes", category: "category", id: "id" },
{ title: "title_1", userID: "userID_1", password: "password_1", notes: "notes_1", category: "category_1", id: "id_1" },
{ title: "title_2", userID: "userID_2", password: "password_2", notes: "notes_2", category: "category_2", id: "id_2" }
];
// You can mostly ignore this. It's just a mock for the db
const db = {
tx: {
// AFAIK if there is a transaction it's possible to execute multiple statements
executeSql: function(sql, params, success, error) {
// just for simulating an error
if (params.title === "title_2") {
error(new Error("Some sql error"));
} else {
console.log(sql, params.title);
success();
}
}
},
transaction: function(tx, error) {
// simulating async
setTimeout(() => {
return tx(this.tx);
}, parseInt(Math.random() * 1000));
}
}
// Lets make a class which handles our dataccess in an async way
class DataAccess {
// as transaction has callback functions it's wrapped in a promise
// on success the transaction is resolved
// if there is an error it will be thrown
transaction = () => {
return new Promise(resolve => {
db.transaction(tx => resolve(tx), error => {
throw error;
});
});
}
// the actual executeSql function which "hides" all the transaction stuff
// awaits a transaction and executes the sql on it
// if the execution was successfull resolve
// if not throw the error
executeSql = async(sql, params) => {
const tx = await this.transaction();
tx.executeSql(sql, params, () => Promise.resolve(), error => {
throw error;
});
}
}
const dal = new DataAccess();
// all sql execute tha was possible
async function insert_with_execute() {
// promise all does not guarantee execution order
// but it is a possibility to await an array of promises (async functions)
await Promise.all(rawData.map(async rd => {
try {
await dal.executeSql("sql_execute", rd);
} catch (error) {
console.log(error.message);
}
}));
}
// no sql executed cause of error and all in the same transaction
async function insert_with_transaction() {
const tx = await dal.transaction();
for (let i = 0; i < rawData.length; i++) {
tx.executeSql("sql_transaction", rawData[i], () => console.log("success"), error => console.log(error.message));
}
}
async function test() {
await insert_with_execute();
console.log("---------------------------------")
await insert_with_transaction();
}
test();
Apparently the best approach to take is using anonymous functions that create a separate instance of execution for each value of i. This is a good example of how to do it....
Javascript SQL Insert Loop

How to know when asynchronous forEach is finished

I want to call a callback when both forEach are done. I want to know when all of them are done processing asynchronously and call a callback. console.log("Done") seems to finish before the two forEach
const getDates = () => {
const ref = db.ref("reminders");
const dateTime = new Date();
const currentDate = dateFormat(dateTime, "yyyy-mm-dd");
ref
.orderByChild('date')
.endAt(currentDate)
.once('value', (reminderDates) => {
reminderDates.forEach((singleDate) => {
// iterate over reminder dates
singleDate.forEach( (notificationValues) => {
// iterate over notification codes
if (!notificationValues.key.includes('date')) {
processNotifications(notificationValues, () => {
console.log(`Sent notification reminder at ${notificationValues.key}`);
});
}
});
});
}).catch( (error) => {
console.log(error);
});
console.log("Done")
};
Output
Done
AB000001_AB0977 { subtitle: 'Time to start thinking about making a payment',
title: 'School Semester 1, 2019 School Fees',
userId: 'kXnfHPyxfpeLQ1aCjvl8Pu09sssslou1' } d-ktpdo45SQ:APA91bF5rJtaHvtNUE42GDssssXoOAP_r7omRmsIs44WKnABsMC8lintdoDBzUYrZ5lutEKECwuaOOIQtdZkKW5Apt4A0ssssyZwdl_epdI2dYHkhk0h-Yns6jzlMbIltSHasA40YL725sssL9TmyCd
Sent notification reminder at AB000001_AB0977
From the docs:
once
once(eventType: EventType, successCallback?: function, failureCallbackOrContext?: Object | null, context?: Object | null): Promise<DataSnapshot>
once returns a Promise which means it is asynchronous, therefore the console.log("Done") will be printed before your forEach(). You cannot know when the asynchronous operation will be finished.
Therefore, the best way to solve it is to add console.log("Done") inside the forEach():
.once('value', (reminderDates) => {
reminderDates.forEach((singleDate) => {
// iterate over reminder dates
singleDate.forEach( (notificationValues) => {
// iterate over notification codes
if (!notificationValues.key.includes('date')) {
processNotifications(notificationValues, () => {
console.log(`Sent notification reminder at ${notificationValues.key}`);
console.log("Done");
});
}
});
});
I don't realy use firebase but if you want to wait for multiple asynchronus operations you can use Promise.all
You just have to push inside an array all your async operation. Once it's finish juste write something like :
Promise.all(yourArrayOfPromise)
.then(() => {
console.log('success');
})
.catch(err => {
console.log(err);
})

Async method not waiting for a function - VUE

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;
},

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