How can I console.log if it still fails after all retries done? and console.log if it succeeds, using async-retry package:
const retry = require('async-retry');
async function updateDB(updateUser) {
await retry(async () => {
const res = await updateUser;
if (/* some error after finishing all retries*/) {
console.log('failed');
}
console.log('Success');
}, {
retries: 5
});
}
how can this be achieved?
or in other words, how can I call another function (function A) only after all retries attempts failed? and call function B if it didn't throw at all.
const retry = require('async-retry');
async function updateDB(updateUser) {
try{
const result = await retry(async () => {
const res = await Promise.all(updateUser).then(()=>{
try{
return new Promise((resolve) => resolve('OK')), { retries: 5 }
}catch(e){
return new Promise((resolve) => resolve('KO')), { retries: 5 };
}
});
}
);
}catch(err){
console.log('The function execution failed !')
}
}
you can use function onRetry, something like this
const retry = require('async-retry');
async function updateDB(updateUser) {
await retry(async () => {
const res = await test();
console.log('Success');
}, {
onRetry: (err, number) => {
console.log('attempt', number)
if (number === 5) {
console.log(err)
}
},
retries: 5
});
}
Have you tried something like:
const result = await retry(async () => {
const res = await updateUser;
if (/* some error after finishing all retries*/) {
console.log('failed');
}
console.log('Success');
return 'success';
}, {
retries: 5
});
if (result != 'success') {
console.log('all failed');
}
Related
I am calling a smart contract function to get some status which is the same as calling an API. And, I need to check if status.fulfilled===true before returning it to front-end. To do this I need to call the API every second to return the result as soon as possible. It usually takes 5-20 seconds for it to be fulfilled.
Here is how i tried to do it:
async function getStatus(requestId) {
try {
await Moralis.enableWeb3({ provider: 'metamask' });
const options = {
contractAddress: coinFlipAddress,
functionName: 'getStatus',
abi: coinFlipABI,
params: { requestId },
};
var status = await Moralis.executeFunction(options);
console.log(status);
if (status.fulfilled) {
console.log('fulfilled');
return status;
} else {
setTimeout(async () => {
return await getStatus(requestId);
}, 1000);
}
} catch (err) {
console.log(err);
return { error: err };
}
}
This keeps calling the getStatus function recursively until status.fulfilled===trueand console.log('fulfilled'); also logs when it is fulfilled, but it doesn't return it to where It is first initialized.
const handleFlip = async (choice) => {
setCurrentChoice(null);
setMetamaskInProgress(true);
const transaction = await flip(choice, amount);
setMetamaskInProgress(false);
setCurrentChoice(choices[choice]);
setFlipping(true);
setResult('Spinning');
const requestId = waitForConfirmation(transaction);
const result = await getStatus(requestId); //This is the initial call to getStatus()
console.log('RESULT ' + result);
if (result) {
setFlipping(false);
setSide(result.hasWon ? (choice === '0' ? 'Heads' : 'Tails') : choice === '0' ? 'Tails' : 'Heads');
setResult(result.hasWon ? 'You have won!' : 'You have lost :(');
}
};
What am I doing wrong? Also, could this recursive calls create any problems with memory? If yes, do you have any suggestions to handle this case differently?
You cannot return from a setTimeout callback. You'll need to promisify that, wait, and return afterwards:
async function getStatus(requestId) {
try {
await Moralis.enableWeb3({ provider: 'metamask' });
const options = {
contractAddress: coinFlipAddress,
functionName: 'getStatus',
abi: coinFlipABI,
params: { requestId },
};
var status = await Moralis.executeFunction(options);
console.log(status);
if (status.fulfilled) {
console.log('fulfilled');
return status;
} else {
await new Promise(resolve => {
setTimeout(resolve, 1000);
});
return getStatus(requestId);
}
} catch (err) {
console.log(err);
return { error: err };
}
}
I would have done something like this :
const MAX_RETRIES = 10; //maximum retries
const REQUEST_DELAY = 1000; //delay between requests (milliseconds)
// JS Implementation of the wide known `sleep` function
const sleep = (time) => new Promise(res => setTimeout(res, time, "done."));
/**
* Retrieve the status
* #param {string} requestId
*/
const getStatus = async (requestId) => {
try {
await Moralis.enableWeb3({ provider: 'metamask' }); //Guessing you need to call this only once
const options = {
contractAddress: coinFlipAddress,
functionName: 'getStatus',
abi: coinFlipABI,
params: { requestId },
};
let retries = 0;
while(retries < MAX_RETRIES) {
let status = await Moralis.executeFunction(options); // check status
console.log('attemtp %d | status %s', retries, status);
if (status.fulfilled) {
return status
}
await sleep(REQUEST_DELAY);
retries++;
}
throw new Error('Unable to retrieve status in time');
} catch (error) {
console.error('Error while fetching status', error);
throw error;
}
}
Few notes here :
I took the constants out of the function for more clarity,
use the while to loop for the number of retries.
Used a widely known 'sleep' method to create a delay between requests, and throw an error whenever something's not supposed to happen, happens (up to you to edit according to your needs).
Finally I used arrow functions for simplicity of use, know it's up to you ;)
You could try this change maxRetry and spaceBetweenRetry as you wish
async function getStatus(requestId) {
return new Promise(async (resolve, reject) => {
try {
let maxRetry = 10; //how many times you want to retry
let spaceBetweenRetry = 1000; // sleep between retries in ms
await Moralis.enableWeb3({ provider: 'metamask' }); //Guessing you need to call this only once
const options = {
contractAddress: coinFlipAddress,
functionName: 'getStatus',
abi: coinFlipABI,
params: { requestId },
};
for (let index = 0; index < maxRetry; index++) {
var status = await Moralis.executeFunction(options); // check status
console.log(status);
if (status.fulfilled) {
resolve(status) //return the promise if fullfilled.
}
await new Promise((r) => setTimeout(r, spaceBetweenRetry)); // sleep for spaceBetweenRetry ms
}
} catch (err) {
reject(err)
}
})}
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])
I am trying to call some function using a single express router , I want to call them in order, meaning that I don't want getLaps() function to execute before get streams function has done all the work , so I tried to use some solutions I found on the internet but it didn't work, the second function doesn't execute. Please help.
Here is my code :
router.get("/", async (req, res,done) => {
res.status(201).send('created user')
return getLaps(function () {
getStreams(function () {
});
});
// await getStreams();
// await getLaps();
// console.log("hey")
});
Here is the get laps function :
function getLaps(req) {
const access_token = '75f2d92fdc445033312854d775e039b6c5bf04e7';
//for test 3756582581,
const idL = [5567017025, 5566531480];
const stravaClient = StravaClientService.getClient(access_token);
const activityService = StravaActivityService(stravaClient);
var params = {
TableName: "run-id",
Key: {
"id": "15428785",
}
};
console.log("cool laps")
docClient.get(params, async function (err, data) {
if (err) {
console.log("Error", err);
} else {
}
idL.map((id, index) => setTimeout(() => activityService.listLaps(id), (5 + index) * 60)
)
//data.Item.json
});
}
and the streams function :
function getStreams(req) {
const idS = [
5567017025, 5566531480
];
const stravaClient = StravaClientService.getClient(access_token);
const activityService = StravaActivityService(stravaClient);
var params = {
TableName: "run-id",
Key: {
"id": "15428785",
}
};
console.log("cool streams")
docClient.get(params, async function (err, data) {
if (err) {
console.log("Error", err);
} else {
idS.map((id, index) => setTimeout(() => activityService.streamActivity(id), (5 + index) * 60))
console.log("got the streams")
}
});
}
in your getStream and getLaps function return promises instead of other object/Stuff like
async function getStream(){
return new Promise(async (resolve, reject){
//Do something
//where you want to return something just call resolve function like
resolve()
//if you want some output of getStream() just pass it to resolve function
//const result = 'I'm result'
resolve(result)
})
}
do same thing with the laps function and in your router call them with await keyword
I am trying to get the result of an async function in my calculateOrderAmount function but it returns undefined.
The console.log in the called function returns the good result, but inside calculateOrderAmount, I get undefined. Here is my code:
getMultiStrats = async () => {
await MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return err
}
if(!multiStrats) {
return console.log('MultiStrat not found')
}
console.log('returns MultiStrat: ' + multiStrats)
return multiStrats
})
.catch(err => console.log(err))
}
async function calculateOrderAmount(balance, ticker){
const multiState = await StrategyController.getMultiStrats().catch((err) => console.log(err))
console.log('multiState: ' + multiState)
some logic
}
Here is the console log:
multiState: undefined
returns MultiStrat: {
_id: 5ff73c74d1135b39fc709b80,
positionsCount: 1,
inTradeCount: 0,
__v: 0
}
What did I miss? Thanks you very much for your time!
The current approach is pretty unclear--there's no need for .catch, async, await all at once. return multiStrats returns from the inside of the callback, not from getMultiStrats. The async/await on getMultiStrats is superfluous, just adding another promise wrapper that doesn't accomplish anything.
Given that findOne as shown here uses a callback rather than a promise, you can either use callbacks all the way or you can promisify findOne as follows, using .then and .catch in the caller:
const MultiStrats = {
findOne: (obj, cb) => cb(null, "I'm a multistrat!")
};
const StrategyController = {
getMultiStrats: () => new Promise((resolve, reject) =>
MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return reject(err);
}
else if (multiStrats) {
return resolve(multiStrats);
}
reject(Error("Multistrat not found"));
})
)
};
const calculateOrderAmount = (balance, ticker) =>
StrategyController
.getMultiStrats()
.then(multiState => {
console.log('multiState: ' + multiState)
// some logic
})
.catch(err => console.error(err))
;
calculateOrderAmount();
Or use async/await and try/catch:
const MultiStrats = {
findOne: (obj, cb) => cb(null, "I'm a multistrat!")
};
const StrategyController = {
getMultiStrats: () => new Promise((resolve, reject) =>
MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return reject(err);
}
else if (multiStrats) {
return resolve(multiStrats);
}
reject(Error("Multistrat not found"));
})
)
};
const calculateOrderAmount = async (balance, ticker) => {
try {
const multiState = await StrategyController.getMultiStrats();
console.log('multiState: ' + multiState)
// some logic
}
catch (err) {
console.error(err);
}
};
calculateOrderAmount();
If this is MongoDB's findOne and already returns a promise, then you can just return the promise to the caller, optionally awaiting it and throwing for the null result:
const MultiStrats = {
findOne: async query => "I'm a multistrat!"
};
const StrategyController = {
getMultiStrats: async () => {
const result = await MultiStrats.findOne({});
if (result) {
return result;
}
throw Error("Multistrat not found");
}
};
const calculateOrderAmount = (balance, ticker) =>
StrategyController
.getMultiStrats()
.then(multiState => {
console.log('multiState: ' + multiState);
// some logic
})
.catch(err => console.error(err))
;
calculateOrderAmount();
You cannot return values from an inner callback and reach the outer function, I would suggest
1- only use promises
2- wrap your code with promise to be sure that await will return the async result the way you expect to have.
getMultiStrats = async () => {
return new Promise((resolve, reject) => {
MultiStrats.findOne({}, (err, multiStrats) => {
if (err) {
return err
}
if (!multiStrats) {
console.log('MultiStrat not found')
reject('MultiStrat not found')
}
console.log('returns MultiStrat: ' + multiStrats)
resolve(multiStrats);
})
.catch(err => {
console.log(err);
reject(err)
})
})
}
async function calculateOrderAmount(balance, ticker) {
try {
const multiState = await StrategyController.getMultiStrats()
console.log('multiState: ' + multiState)
// some logic
} catch (error) {
console.error(error);
}
}
Assuming that you use mongoose. I suggest using the promise interface like described in the documentation.
const getMultiStrats = async () => {
const query = MultiStrats.findOne({});
let multiStrats;
try {
multiStrats = await query.exec();
} catch (error) {
return error;
}
if (multiStrats) {
console.log("returns MultiStrat: " + multiStrats);
} else {
console.log("MultiStrat not found");
}
return multiStrats;
}
I would personally not return the error, but instead just let the error be thrown. With the above code the caller of getMultiStrats has to figure out if there return value is the expected result or an error. If you don't catch the error, it is thrown further up to the caller.
const getMultiStrats = async () => {
const multiStrats = await MultiStrats.findOne({}).exec();
if (multiStrats) {
console.log("returns MultiStrat: " + multiStrats);
} else {
console.log("MultiStrat not found");
}
return multiStrats;
}
You can further simplify this if you where to leave the console.log of of the equation.
const getMultiStrats = () => MultiStrats.findOne({}).exec();
First, I know this is a common question. I'm trying to get a handle on how to use async / await in place of setTimeouts, but all the examples I see online use a setTimeout to simulate the async. This throws me off when it's a set timeout that I'm trying to replace.
In the function below, I want this.filteredResultsto await the results of an API call before trying to filter those results and assign it to this.filteredResults.
getResults() {
let allResults= airtableQuery.getTable("Transfers"); // API call using imported 'getTable' function
console.log(allResults); // returns full array ▶[] although it's not available for filtering yet.
setTimeout(() => { // I want to replace this timeout
this.filteredResults = allResults.filter(
(result) => result.fields.User === "dev"
);
}, 250); // random ms that is roughly how long airtableQuery takes for the API call.
},
And the airtableQuery:
getTable(table) {
let recordsArr = [];
base(`${table}`)
.select({
maxRecords: 8000,
})
.eachPage(
function page(records, fetchNextPage) {
records.forEach((record) => {
recordsArr.push(record);
});
fetchNextPage();
},
function done(err) {
if (err) {
this.$toasted.error(err);
}
}
);
return recordsArr;
},
Please make the outer function an async function and then await the results before filtering them.
async function getResults() {
let allResults = await airtableQuery.getTable("Transfers");
this.filteredResults = allResults.filter(
(result) => result.fields.User === "dev"
);
},
Given that getTable() is not a Promise, await will not do anything. For that reason, we can make getTable() return a Promise which will resolve with recordsArr.
getTable(table) {
return new Promise((resolve, reject) => {
let recordsArr = [];
base(`${table}`)
.select({
maxRecords: 8000,
})
.eachPage(
function page(records, fetchNextPage) {
records.forEach((record) => {
recordsArr.push(record);
});
fetchNextPage();
},
function done(err) {
if (err) {
this.$toasted.error(err);
reject(err)
}else {
resolve(recordsArr)
}
}
);
})
}
Hope it helps.
i always likes primise,this my code show you
getTable(table) {
return new Promise((res, rej) => {
let recordsArr = [];
base(`${table}`)
.select({
maxRecords: 8000,
})
.eachPage(
function page(records, fetchNextPage) {
records.forEach((record) => {
recordsArr.push(record);
});
fetchNextPage();
res(recordsArr)
},
function done(err) {
if (err) {
this.$toasted.error(err);
rej(err)
}
}
);
})
}
getResults() {
airtableQuery.getTable("Transfers").then(res => {
let allResults = res
console.log(allResults);
this.filteredResults = allResults.filter(
(result) => result.fields.User === "dev"
);
});
}