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

I have a function with try catch inside. I call to the API about response. This response returns me a Result object with some data. But sometimes I'd like to retry the function if I have no name and have bundle_id.
I'd like to retry this functions max 3 times. If no result, I'd like to throw an error.
I have sth like this, but it doesn't work for me.
const getECommerceProduct = async ({
item,
getParent = false,
variantInfo,
}) => {
if (!item.id) return null;
let retryCounter = 0;
try {
const response = await getEvaService(Core.GetProductDetail, {
ID: item.id,
});
const itemResponse = response?.Result;
if (!itemResponse) return null;
// Retry the function if the item has no name and has a bundle product.
// The bundle product will give back more information about the product.
const retry = !!itemResponse.bundle_id && !itemResponse.product_name;
if (retry && !getParent && retryCounter <= 3) {
retryCounter += 1;
return getECommerceProduct({
item: {
id: itemResponse.bundle_id,
quantity: item.quantity,
variantInfo,
},
getParent: true,
});
}
return transformProductToECommerce(itemResponse, item);
} catch (e) {
console.error(e);
return null;
}
};
Could you help me?

You could use a state to track the attempt count.
[retry_count, setRetryCount] = useState(0);
useEffect(() => {
if (retry_count < 3) {
// .... put fetch code here
}
}, [retry_count])
Increment the retry_count every time by putting it in the catch block within your fetch function
React.useEffect(() => {
const getECommerceProduct = async ({ item, getParent = false, variantInfo }) => {
if (!item.id) return null;
const response = await getEvaService(Core.GetProductDetail, {
ID: item.id,
});
const itemResponse = response?.Result;
if (!itemResponse) return null;
return transformProductToECommerce(itemResponse, item);
};
if (retry_count < 3) {
getECommerceProduct().catch((e) => {
console.error(e);
setRetryCount(retry_count + 1);
return null;
});
}
}, [retry_count]);
With the useEffect, this means that every time the retry_count is incremented (in this case an error happens), the function within the useEffect call will be performed.

Related

can i make the async.retry method retry even on successfull queries but based on a condition

I'm studying the node.js module async,I want to find out if there is a way to change the async.retry method to retry even on successfull operations but stop based on some condition or response let's say its an api call.
According to its docs ,the function will continue trying the task on failures until it succeeds.if it succeeds it will only run only that time But how can i make it work the same on successfull operations and make it stop on some condition ?
const async = require('async');
const axios = require('axios');
const api = async () => {
const uri = 'https://jsonplaceholder.typicode.com/todos/1';
try {
const results = await axios.get(uri);
return results.data;
} catch (error) {
throw error;
}
};
const retryPolicy = async (apiMethod) => {
async.retry({ times: 3, interval: 200 }, apiMethod, function (err, result) {
// should retry untill the condition is met
if (result.data.userId == 5) {
// stop retring
}
});
};
retryPolicy(api);
Yes, You can just throw a custom error if condition is not met. Would be something like that:
const async = require('async');
const axios = require('axios');
const api = async () => {
const uri = 'https://jsonplaceholder.typicode.com/todos/1';
try {
const results = await axios.get(uri);
if(typeof result.data.userId != 'undefined' && result.data.userId == 5){ // change this condition to fit your needs
return results.data;
}else{
throw {name : "BadDataError", message : "I don't like the data I got"};
}
} catch (error) {
throw error;
}
};
I don't think this is possible.
On the async.retry documentation you can find this description:
Attempts to get a successful response from task no more than times
times before returning an error. If the task is successful, the
callback will be passed the result of the successful task. If all
attempts fail, the callback will be passed the error and result (if
any) of the final attempt.
However, using the delay function given here, you can do what you want another way:
const async = require('async');
const axios = require('axios');
const delay = (t, val) => {
return new Promise((resolve) => {
setTimeout(() => { resolve(val) }, t);
});
}
const api = async () => {
const uri = 'https://jsonplaceholder.typicode.com/todos/1';
try {
const results = await axios.get(uri);
return results.data;
} catch (error) {
throw error;
}
};
const retryPolicy = async (apiMethod) => {
const times = 3
const interval = 200
let data
for (count = 0; count < 3; count++) {
try {
data = await apiMethod()
catch(e) {
console.log(e)
await delay(interval)
continue
}
if (data.userId === 5) {
break;
}
await delay(interval)
}
// do something
};
retryPolicy(api);

Wait loop to finish first - Typescript (Angular)

So I have this code here triggered using click on HTML page:
public salaryConfirmation() {
const matDialogConfig: MatDialogConfig = _.cloneDeep(GajiIdSettings.DIALOG_CONFIG);
this.warningNameList = [];
for(let i=0; i < this.kelolaDataPenggajianInfoDataKaryawanList.length; i++) {
const positionClassId = this.selectedKaryawanAllData[i].position.positionClass.id;
const beginYearMonth = this.inputForm.get('bulanBerlaku').value;
const gajiPokok = this.kelolaDataPenggajianInfoDataKaryawanList[i].gaji;
this.structureAndSalaryScaleValidationService.getSalaryRange(positionClassId, beginYearMonth, gajiPokok)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe(
async (result) => {
this.uiBlockService.hideUiBlock();
if(result.status == 'warning') {
if(result.warnings[0].code == 'trxMutasiKaryawan.confirmation.alert') {
await this.warningNameList.push(this.kelolaDataPenggajianInfoDataKaryawanList[i]);
}
}
},
(error) => {
this.uiBlockService.hideUiBlock();
this.contentAlertService.error(error.errors);
},
() => { this.uiBlockService.hideUiBlock(); }
)
}
matDialogConfig.data = this.warningNameList;
console.log("this.warningNameList.length :", this.warningNameList.length);
if (this.warningNameList.length > 0) {
this.save();
} else {
this.inputMassalGajiWarningComponentDialogRef = this.dialog.open(InputMassalGajiWarningComponent, matDialogConfig);
this.inputMassalGajiWarningComponentDialogRef.afterClosed().subscribe(
(confirm: boolean) => {
if (confirm) {
this.save();
}
}
);
}
}
The problem is, I tried to catch the length of this.warningNameList variable. But it always shows "0" on the result.
I know the problem is because this should be work in asynchronously. But I don't know how to apply it in typescript. Been search this case but I always failed to apply. Anyone can help me out?
I already put await inside the loop, but seems it's not working because of wrong placement.
Some reference that I found out there is this => JavaScript async and await in loops
Thanks in advance
Putting await inside loop is not gonna help you. as it is running in a different context already.
What you need to do is probably chain this 2 operations one after another after using promise instead of observable here.
you can probably do something like this,
public async salaryConfirmation() {
const matDialogConfig: MatDialogConfig = _.cloneDeep(GajiIdSettings.DIALOG_CONFIG);
this.warningNameList = [];
for(let i=0; i < this.kelolaDataPenggajianInfoDataKaryawanList.length; i++) {
const positionClassId = this.selectedKaryawanAllData[i].position.positionClass.id;
const beginYearMonth = this.inputForm.get('bulanBerlaku').value;
const gajiPokok = this.kelolaDataPenggajianInfoDataKaryawanList[i].gaji;
let result = await this.structureAndSalaryScaleValidationService.getSalaryRange(positionClassId, beginYearMonth, gajiPokok)
.pipe(takeUntil(this.ngUnsubscribe)).toPromise();
// Transform following data
// .subscribe(
// async (result) => {
// this.uiBlockService.hideUiBlock();
// if(result.status == 'warning') {
// if(result.warnings[0].code == 'trxMutasiKaryawan.confirmation.alert') {
// await this.warningNameList.push(this.kelolaDataPenggajianInfoDataKaryawanList[i]);
// }
// }
// },
// (error) => {
// this.uiBlockService.hideUiBlock();
// this.contentAlertService.error(error.errors);
// },
// () => { this.uiBlockService.hideUiBlock(); }
// )
}
matDialogConfig.data = this.warningNameList;
console.log("this.warningNameList.length :", this.warningNameList.length);
if (this.warningNameList.length > 0) {
this.save();
} else {
this.inputMassalGajiWarningComponentDialogRef = this.dialog.open(InputMassalGajiWarningComponent, matDialogConfig);
this.inputMassalGajiWarningComponentDialogRef.afterClosed().subscribe(
(confirm: boolean) => {
if (confirm) {
this.save();
}
}
);
}
}
Just like above code convert observables to promise, don't subscribe them.
This way you can use async await syntax with obervables as well, although find out how to handle errors this way.

How to make an api call inside of a map function with delay between each call on that api?

This second api call inside of the map function needs to be called in a space of time, because this api does not allow multiple calls at the time. So, the map for each item inside of the array will take two seconds to call the api and after it go to the next item.
How can i fix it?
It does not return anything.
async function HandleMatchList(){
try{
const responseMatches = await api.get('MatchListRankedGames', {
params: {
nickname
}
})
const matches = responseMatches.data
const Awaitfor2seconds = (x) => {
return new Promise (resolve => {
setTimeout(() => {
resolve(x)
}, 5000)
})
}
const linking = async (matches) => {
matches.map(async item => {
const details = await Awaitfor2seconds(
api.get('MatchDetailRoute', {
params: {
gameId: item.gameId,
nickname: nickname
}
}).then(({data}) => {
data
})
)
return details
})
}
linking(matches).then(results => {
setMatches(results)
})
}catch(e){
setError(e)
}
}
You can follow this concept (no tested):
const matches = responseMatches.data
var count = 0 // create a counter
const Awaitfor2seconds = (x) => {
return new Promise (resolve => {
count++ // count++ is the same thing that: count = count + 1
setTimeout(() => {
resolve(x)
}, 5000*count) // using this the request will be send like a queue
})
}
I suggest you make a sleep function separate and then you call it whenever you want to pause your API call
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
try{
const responseMatches = await api.get('MatchListRankedGames', {
params: {
nickname
}
})
const matches = responseMatches.data
await sleep(5000)
const linking = async (matches) => {
results=[]
for(let item of matches){
var details= await api.get('MatchDetailRoute', {
params: {
gameId: item.gameId,
nickname: nickname
}
})
results.push(details)
await sleep(5000)
}
return results
}
linking(matches).then(results => {
setMatches(results)
})
}catch(e){
setError(e)
}

Async Storage returning function instead of values

I'm trying to use AsyncStorage in my react native app and dont know why is not working.
Basically I want to have an indexed array (or a any key-value pair) stored in asyncstorage with true or false for every element that have been added.
import {AsyncStorage} from 'react-native';
....
componentDidMount() {
this.storeData('favourites', []);
}
addOrRemove(id) {
let favourites = this.getData('favourites');
console.log('favourites getted: ', favourites);
favourites[id] = favourites[id] ? false : true; //this logic is working fine
this.storeData('favourites', favourites);
}
getData and storeData:
storeData = (key, value) => async () => {
try {
await AsyncStorage.setItem(key, value);
} catch (e) {
// saving error
}
};
getData = key => async () => {
try {
const value = await AsyncStorage.getItem(key)
return value;
} catch(e) {
// error reading value
}
};
This is what I get when I do console.log('favourites getted: ', favourites);
favourites getted: function _callee2() {
var value;
return _regenerator.default.async(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
_context2.prev = 0;
_context2.next = 3;
return _regenerator.default.awrap(_reactNative.AsyncStorage.getItem(key));
case 3:
value = _context2.sent;
return _context2.abrupt("return", value);
case 7:
_context2.prev = 7;
_context2.t0 = _context2["catch"](0);
case 9:
case "end":
return _context2.stop();
}
}
}, null, null, [[0, 7]]);
}
When someone clicks on a specific button the method addOrRemove(id) is triggered. I want to get the array that I have stored in my AsyncStorage and put true or false in the id position of that array.
Why I'm receiving that function from the AsyncStorage and not the indexed array that I want?
I think that can be an async/await problem, but don't know where is the issue.
Your function "storeData" and "getData" return a async function, you can simplify :
storeData = async (key, value) => {
try {
await AsyncStorage.setItem(key, value);
} catch (e) {
// process error
}
};
getData = async (key) => {
try {
const value = await AsyncStorage.getItem(key)
return value;
} catch (e) {
// process error
}
};
And use them with async/await :
componentDidMount() {
this.storeData('favourites', []);
}
async addOrRemove(id) {
try {
let favourites = await this.getData('favourites');
console.log('favourites getted: ', favourites);
favourites[id] = favourites[id] ? false : true;
await this.storeData('favourites', favourites);
} catch (err) {
//process error
}
}

JavaScript: Returning array from recursive function

I have made a class which builds some data from api:
const http = require("http");
class VideoService {
constructor() {
this.items = [];
}
fetchVideos(token = "") {
const url = `https://www.example.com`;
http.getJSON(url).then((results) => {
results.items.forEach((item, index) => {
const vid = item.snippet.resourceId.videoId;
this.items.push({
title: item.title,
date: item.publishedAt
});
console.log(this.items.length); // here length inreases, works here
});
if (typeof results.nextPageToken !== "undefined") {
return this.fetchVideos(results.nextPageToken);
}
});
}
getVideos() {
this.fetchVideos();
console.log(this.items.length); // this returns 0 instead of all items fetched
return this.items;
}
}
module.exports = VideoService;
In another file, I am using it like this:
const videoService = require("../shared/videoService");
const videos = (new videoService()).getVideos();
console.log(videos);
The last console.log call always returns empty array instead of all data collected in items property of the above class.
Can anybody tell what I am missing here?
This happens because in your function fetchVideos(), you are making an http call which will be processed asynchronously. You can try to process it this way.
fetchVideos(token = "") {
const url = `https://www.example.com`;
return http.getJSON(url).then((results) => {
results.items.forEach((item, index) => {
const vid = item.snippet.resourceId.videoId;
this.items.push({
title: item.title,
date: item.publishedAt
});
console.log(this.items.length); // here length inreases, works here
});
if (typeof results.nextPageToken !== "undefined") {
return this.fetchVideos(results.nextPageToken);
}
else return new Promise((resolve, reject)=>{
resolve();
});
});
}
getVideos() {
return this.fetchVideos().then(function(){
console.log(this.items.length); // this returns 0 instead of all items fetched
return this.items;
});
}
I suggest reading about promises and asynchronicity in javascript. Check this link:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Categories