how to call second API after first API result is retrieved - javascript

I have a API which called the bank end :
this.http.get(`http://localhost:44301/consentinitiation/${this.qid}`)
.pipe(retryWhen(_ => {
this.showIt=true
return interval(1000)
}))
.subscribe(result => {result
console.log(result);
this.qrcodelink=result["qrCodeLink"];
setTimeout(() => {
this.loadingSpinner=false;
}, 5000);
})
It has a result and has a status which is "Recieved" after that i should call the API again unstil i get the status "Finalized" and dont how to call the API again ,after the first call is finished,because if i write it below the first one i guess they will call the simultaneously,any idea?

the problem that u describe in description is called polling ( make request in interval until u got an expected result )
here is poll implementation in rxjs way
makeSomeRequestsToBank() {
this.http.get('https://').pipe(
switchMap(result => {
// if status is recieved, start polling
if (result.status === 'Recieved') {
return this.pollStatus();
}
if (result.status === 'Finalized') {
return of(result)
}
// else do some thing else, depends on u business logic
// keep in mind that switchMap should return an observable for futher chaining
return of(undefined);
}),
).subscribe(result => {
if (!result) return;
this.qrcodelink=result["qrCodeLink"];
setTimeout(() => {
this.loadingSpinner=false;
}, 5000);
}
pollStatus(): Observable<any> {
const POLLING_INTERVAL = 700; // poll in ms
const isSuccessFn = (response: string) => response === 'Finalized'; // the condition to stop polling and return result
return timer(0, POLLING_INTERVAL).pipe(
switchMap(() => this.http.get('https://')),
skipWhile(response => isSuccessFn(response)),
);
}

You can do it with using Promise.
ngOnInit(){
this.callFirstApi();
}
firstApi(): Promise<any> {
return new Promise((resolve, reject) => {
this.http.get(API_URL).subscribe((data) => {
resolve(data);
}, (error) => {
reject(error);
});
});
}
secApi(): Promise<any> {
return new Promise((resolve, reject) => {
this.http.get(API_URL).subscribe((data) => {
resolve(data);
}, (error) => {
reject(error);
});
});
}
callFirstApi(){
this.firstApi().then(response => {
this.callSecApi();
}).catch(err => {
})
}
callSecApi(){
this.secApi().then(response => {
}).catch(err => {
})
}

Related

How can I retry a function with try/catch promise in react js?

I need help with a performative way to retry if the return is 500, to try again
What would be the most viable solution in this case?
useEffect(() => {
setCSV(createPayload())
;(async () => {
try {
setIsLoadingGraph(true)
const response = await api.generateDataByFilters(createPayload())
response.data.itens.forEach((data) => {
setDataTable(data.detail)
setDataGraph(data.summarizedBySenderNumber)
setTotalDispatchMessages(data.totalDispatchMessages)
setTotalSentMessages(data.totalSentMessages)
setTotalReceivedMessages(data.totalReceivedMessages)
setTotalReadMessages(data.totalReadMessages)
setTotalFailedMessages(data.totalFailedMessages)
setTotalBillableMessages(data.totalBillableMessages)
})
setIsLoadingGraph(false)
} catch (error) {
console.error(error)
}
})()
}, [periodFilter, originFilter, senderFilter, endDateTime, initialDateTime])
Just give the function a name, then in the catch method, if the status === 500, re-execute the function again, Also you can give a throttling between each request by for example 2 seconds.
useEffect(() => {
setCSV(createPayload())
const generateDataByFilters = async () => {
try {
setIsLoadingGraph(true)
const response = await api.generateDataByFilters(createPayload())
response.data.itens.forEach((data) => {
setDataTable(data.detail)
setDataGraph(data.summarizedBySenderNumber)
setTotalDispatchMessages(data.totalDispatchMessages)
setTotalSentMessages(data.totalSentMessages)
setTotalReceivedMessages(data.totalReceivedMessages)
setTotalReadMessages(data.totalReadMessages)
setTotalFailedMessages(data.totalFailedMessages)
setTotalBillableMessages(data.totalBillableMessages)
})
setIsLoadingGraph(false)
} catch (error) {
console.error(error)
if (error.status === 500) {
await new Promise((resolve, reject) => {
setTimeout(() => {
generateDataByFilters()
resolve()
}, 2000)
})
}
}
}
}, [periodFilter, originFilter, senderFilter, endDateTime, initialDateTime])

How to make two api calls using Promise.all within Angular9?

I making an api call using Promise.all as below:
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
The above code is working fine.
Now I want to make another api call after the successful completion of this.serviceB.retry(oretry).
The second api is this.serviceB.createDbEntry(sentry) and sentry looks as below:
const sretry: SDInterface = {
hostName,
Id: this.Id.slice(0, this.Id.length),
reason: this.reason
};
And, I am doing it as below
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
const sretry: SDInterface = {
hostName,
Id: this.Id.slice(0, this.Id.length),
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
The above code is giving an error:
error: "SequelizeValidationError: string violation: Id cannot be an array or an object"
It is looks like it is not calling the second api for every Id
You may want to take a look a forkJoin
import { Observable, forkJoin } from 'rxjs';
And then
ngOnInit() {
let one = this.http.get('some/api/1') //some observable;
let two = this.http.get('some/api/2') // another observable;
forkJoin([one, tow]).subscribe(response => {
// results[0] is our one call
// results[1] is our second call
let var1 = response[1];
let var2 = response[0];
}/*, error => { in case error handler } */);
}
Wouldn't it be better to use Promise.all() once more?
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
})
.then(() => {
return Promise.all(this.Id.slice(0, this.Id.length).map(id => {
return new Promise((resolve, reject) => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
})
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
And using toPromise() will make the code more concise.
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
return this.serviceB.retry(oretry).toPromise();
})
.then(() => {
return Promise.all(this.Id.slice(0, this.Id.length).map(id => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).toPromise();
})
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
Use combineLatest, in Angular we use RxJs not promises.
combineLatest(
[this.http.get('call1'), this.http.get('call2')]
).subscribe(([result1, result2]) => {
// do stuff with result1 and result2
});
promise.all takes input in an array and gives response in an array,
Create 2 functions each with your asynchronous logic returning a promise,
Say funcA and funcB, then use below to invoke them parellely
Promise.all([funcA(this.hostName), funcB(this.id)])
.then(respones => {
console.log(responses[0]); //return value for funcA
console.log(responses[1]); //return value for funcB
})
.catch(err => console.log(err));
I am assuming your logic of functions are correct, I just copy-pasted from your question and gave them structure
const funcA = (hostName) => {
hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
});
});
}
const funcB = (Id) => {
Id.slice(0, this.Id.length).map(id => {
return new Promise((resolve, reject) => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
})
}

.then() fires before previous .then() has returned

I'm pulling category information from our local point of sale database (3rd party software) and trying to write it into a WooCommerce store. I'm also storing the data to my own database to maintain relationships between the two systems. I'm using promises to sequence everything, but one of my .then() statements is firing off before the previous .then() has returned, so I'm sending an empty payload to WooCommerce.
router.post("/:action", (req, res) => {
if(req.params.action === "sync" && req.body.action === "sync") {
// Query the POS database
mssql.query(query, (err, result) => {
let postData = {
create: [],
update: []
}
// Make some promises to pull the POS categories and their children
Promise.all(promises)
.then(cats => {
let catPromises = cats.map(cat => {
return new Promise((resolve, reject) => {
Category.findOne(
// Check for existing entry in the linking DB...
)
.then(data => {
// ...and handle accordingly
resolve()
})
.then(() => {
let childPromises = cat.children.map(child => {
return new Promise((resolve, reject) => {
Category.findOne(
// Checking for existing entry in the linking DB...
)
.then(data => {
// ...and handle accordingly
resolve()
})
})
})
Promise.all(childPromises)
.then(resolved => {
resolve()
})
})
})
})
Promise.all(catPromises)
.then(() => {
return
})
})
.then(() => {
// This is the part that's firing early
return axios.post(
// data
)
})
...
EDIT: Newly refactored, still having problems.
Promise.all(promises).then(cats => {
let catPromises = cats.map(cat => {
Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
}).then(() => {
let childPromises = cat.children.map(child => {
Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
})
})
return Promise.all(childPromises)
})
})
// Now this is where we're reaching early
return Promise.all(catPromises)
}).then(() => {
// API call
})
Final solution:
Promise.all(promises).then(cats => {
let catPromises = cats.map(cat => {
return Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
}).then(() => {
let childPromises = cat.children.map(child => {
return Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
})
})
return Promise.all(childPromises)
})
})
// Now this is where we're reaching early
return Promise.all(catPromises)
}).then(() => {
// API call
})

async issues with js generator and promises not returning result

I'm having yet another async issue where I'm lost and have no idea where or how to fix it. Forgive my bad naming.
api call to twitch api and returns an array its results.
exports.batchPromiseWrapper = function(arr) {
const filteredMungedDataArr = [];
let promiseBatachArray = arr.map(vod_id => {
var url = `https://api.twitch.tv/kraken/videos/${vod_id.id}/markers`;
var params = { api_version: 5 };
return axios
.get(url, {
params: params,
headers: {
"Client-ID": "xxxxxxxxxxxxxxx"
}
})
.then(res => {
return res.data;
})
.catch(function(error) {
console.log(error);
});
});
return Promise.all(promiseBatachArray)
.then(markers => {
if (markers !== null) {
markers.map(markerObj => {
if (markerObj.markers.game_changes !== null) {
markerObj.markers.game_changes.forEach(gameName => {
if (gameName.label === "Fortnite") {
filteredMungedDataArr.push(markerObj);
}
});
}
});
return filteredMungedDataArr;
}
})
.catch(err => {
if (err.status === 500 || err.status === 404) {
console.log("error: ", err, err.message);
}
});
};
The data looks like this:
[[1,2,3,4,5],[1,2,3,4,5]], generator will yield and make a promise.all call of 5 before pausing 5sec and continuing to the next batch of 5.
exports.batchFetchingGeneratorWrapper = function(generator, batchArray) {
let evalNextValue = generator.next();
let delay = (v, t) => {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
};
if (!evalNextValue.done) {
exports.batchPromiseWrapper(evalNextValue.value).then(data => {
let newBatchArray = batchArray;
if (data !== undefined) {
newBatchArray = batchArray.concat(data);
}
delay(5000).then(() => {
exports.batchFetchingGeneratorWrapper(generator, newBatchArray);
});
});
} else {
console.log("yay done!", batchArray);
return batchArray;
}
};
I'm able to console the results in batchArray from batchFetchingGeneratorWrapper, but I unable to act on it and I know it has something to do with async and how it has yet to be resolved.
promiseDataWrapper
.then(data => {
return gatherData.cleanUpVODData(data);
})
.then(data => {
function* batchFetching(batchArray) {
for (let i = 0; i < batchArray.length; i++) {
yield batchArray[i];
}
}
let batchArrResult = [];
let g = batchFetching(data);
new Promise((resolve, reject) => {
gatherData.batchFetchingGeneratorWrapper(g, batchArrResult);
if (g.done) { // i dont think this works
console.log("batchArrResult 1: ", batchArrResult);
resolve(batchArrResult);
}
}).then(result => console.log("asdfasdf", batchArrResult)); // empty array is returned
});
As far as I can tell, the problem lies chiefly in batchFetchingGeneratorWrapper().
It should be a matter of :
fixing delay()
making appropriate returns to make the recursion work
ensuring that the function returns Promise.
Almost undoubtedly (syntactically) simpler with async/await but here it is with old-fashioned thens :
exports.batchFetchingGeneratorWrapper = function(generator, batchArray) {
let evalNextValue = generator.next();
let delay = (t) => {
return new Promise(resolve => {
setTimeout(resolve, t);
});
};
if (!evalNextValue.done) {
return exports.batchPromiseWrapper(evalNextValue.value).then(data => {
return delay(5000).then(() => {
return exports.batchFetchingGeneratorWrapper(generator, batchArray.concat(data || []));
});
});
} else {
console.log("yay done!", batchArray);
return Promise.resolve(batchArray); // <<< promise wrapped to ensure that batchFetchingGeneratorWrapper() returns Promise
}
};
And chain the batchFetchingGeneratorWrapper() call appropriately :
promiseDataWrapper
.then(data => gatherData.cleanUpVODData(data))
.then(data => {
function* batchFetching(batchArray) {
for (let i = 0; i < batchArray.length; i++) {
yield batchArray[i];
}
}
return gatherData.batchFetchingGeneratorWrapper(batchFetching(data), []).then(batchArrResult => {
console.log('batchArrResult: ', batchArrResult);
return batchArrResult;
});
}).catch(error => {
console.log(error);
});

Promise continues after error

I have some async code that needs to stop in case of error but keeps executing:
async saveCoupons({ state, rootState, dispatch, commit }) {
const promises = []
state.userCoupons.forEach(coupon => {
if (coupon.isNew && coupon.isUpdated) {
// if the user is creating a new coupon
promises.push(Vue.axios.post('/api_producer/coupons.json', coupon, { params: { token: coupon.token } }))
} else if (!coupon.isNew && coupon.isUpdated) {
// if the user is updating the coupon
promises.push(Vue.axios.patch(`api_producer/coupons/${coupon.id}/`, coupon, { params: { token: coupon.token } }))
}
})
try {
await Promise.all(promises)
dispatch('utilities/showModal', 'success', { root: true })
dispatch('fetchProducerCoupons')
} catch (err) {
let couponToken = err.request.responseURL.split('token=')[1]
commit('ADD_ERROR_ON_COUPON', couponToken)
console.log(err)
}
}
This is how the code is currently structured, it works, but I realize it's terrible. What I need to do is stop the excution of
dispatch('utilities/showModal', 'success', { root: true })
dispatch('fetchProducerCoupons')
In case one of the api calls fails. I wanted to catch the error inside the forEach so I already have the item available and I can add the error to it right away as opposed to doing it after (which is what I'm doing now with { params: { token: coupon.token } }.
I think the best way would be to wrap the Vue.axios requests into your own Promise. Then, if the requests fail, you have the coupon tokens in your error.
Something like
const promises = [];
promises.push(
Vue.axios.post('/api_producer/coupons.json', coupon)
.catch(() => { throw new Error(coupon.token) }));
Promise.all(promises).catch(tokens => {
tokens.forEach(token => {
// Commit/handle errorous token
});
});
You can wrap your api call in another promise and check the status. Something like this:
promises.push(
new Promise((resolve, reject) => {
Vue.axios.post('/api_producer/coupons.json', coupon, { params: { token: coupon.token } })
.then((response) => {
if (response.status !== 200) {
coupon.error = true;
reject();
} else {
resolve();
}
});
})
);
The reject will keep these two lines from being executed:
dispatch('utilities/showModal', 'success', { root: true })
dispatch('fetchProducerCoupons')
Thanks to Moritz Schmitz v. Hülst & sklingler93 for the help, I restructured the code and it's working.
I'm wondering if there's a way to write all of this using only async/await... If anybody has an idea, would love to see it :)
saveCoupons({ state, rootState, dispatch, commit }) {
const promises = []
state.userCoupons.forEach(coupon => {
if (coupon.isNew && coupon.isUpdated) {
// if the user is creating a new coupon
promises.push(new Promise((resolve, reject) => {
Vue.axios.post('/api_producer/coupons.json', coupon)
.then(response => resolve(response))
.catch(err => {
reject(err)
commit('ADD_ERROR_ON_COUPON', coupon.token)
})
}))
} else if (!coupon.isNew && coupon.isUpdated) {
// if the user is updating the coupon
promises.push(new Promise((resolve, reject) => {
Vue.axios.patch(`api_producer/coupons/${coupon.id}/`, coupon)
.then(response => resolve(response))
.catch(err => {
reject(err)
commit('ADD_ERROR_ON_COUPON', coupon.token)
})
}))
}
})
Promise.all(promises)
.then(() => {
dispatch('utilities/showModal', 'success', { root: true })
dispatch('fetchProducerCoupons')
})
.catch(err => console.error(err))
},

Categories