Custom Retry Function - javascript

Trying to create a function retry that returns a function that calls and returns value from callback function passing its arguments and catches errors. If error is caught it should return the callback function with catch. If number of errors exceeds count then throw an Error.
Here is what was done so far:
const retry = (count, callback) => {
let attempts = 1;
const _retry = async (...args) => callback(...args)
.catch(err => {
if (attempts > count) throw err
attempts++
return _retry(...args)
});
return _retry
}
The problem appears when called:
var r = Math.random().toString(36).slice(2)
var arg = (n) => async (...v) =>
--n < 0 ? v : Promise.reject(Error(`>>> x:${v}`))
await retry(3, arg(2))(r)

It looks to me like retry returns a Promise right now, due to the async keyword. Try dropping the async keyword from the retry definition and make the _retry function async:
const retry = (count, callback) => {
let attempts = 1;
return _retry = async (...args) => callback(...args)
.catch(err => {
if (attempts > count) throw err
attempts++
return _retry(...args)
});
}

Related

Why Promise is not displaying the data with setTimeout?

I wrote a function to get a random number after 500 milliseconds and it worked perfectly with (return new Promise (resolve,reject)), also, it worked without it by using setTimeout() function, but when I added the promise and setTimeout() inside it wont show the result, I have console.log() before and after the result and both are showing. Please note that i am still new to the promise concept in general since this is a task from my coding school, thank you.
My code:
function getRandomNumber () {
new Promise ((resolve,reject) =>{
console.log('Getting Random Number...');
let number = Math.random();
setTimeout( () => {
if(number){
resolve (`OK ${number}`);
console.log('Done!');
}
else
reject ('ERROR');
}, 500);
});
}
getRandomNumber();
Result:
First, return your promise, second use the result the promise provides either by creating a variable from it and awaiting it or using then
function getRandomNumber() {
return new Promise((resolve, reject) => {
console.log('Getting Random Number...');
let number = Math.random();
setTimeout(() => {
if (number) {
resolve(`OK ${number}`);
console.log('Done!');
} else {
reject('ERROR');
}
}, 500);
});
}
getRandomNumber().then(result => console.log(result));
(async () => {
const number = await getRandomNumber();
console.log(number);
})();
Another option is to write sleep as a separate reusable function. Note async function can await multiple values and return values of their own. Calling an async function will always return a Promise -
function sleep(time) {
return new Promise(resolve => setTimeout(resolve, time))
}
async function getRandomNumber() {
console.log('Getting Random Number...')
await sleep(500)
const number = Math.random()
console.log("Done!")
return number
}
async function main() {
const number1 = await getRandomNumber()
console.log("number1", number1)
const number2 = await getRandomNumber()
console.log("number1", number2)
const number3 = await getRandomNumber()
console.log("number1", number3)
return number1 + number2 + number3
}
main().then(console.log, console.error)
.as-console-wrapper { min-height: 100%; }
Getting Random Number...
Done!
number1 0.2907058138817884
Getting Random Number...
Done!
number1 0.05784624607512423
Getting Random Number...
Done!
number1 0.889702848981964
1.2382549089388766
You need to return the promise then you can use await inside async function to get the result
function getRandomNumber () {
return new Promise ((resolve,reject) =>{
console.log('Getting Random Number...');
let number = Math.random();
setTimeout( () => {
if(number){
resolve (`OK ${number}`);
console.log('Done!');
}
else
reject ('ERROR');
}, 500);
});
}
const printNumber = async () => console.log(await getRandomNumber());
printNumber();

JavaScript: async function that would retry for a period of time at a given interval

I am trying to write an async function that is going to await a function that got passed as one parameter and I wanted this async function to retry this operation for 5 mins every 10 seconds.
I found one function that sort of does this but it retries based on the numbers of times instead.
async function retry(fn, n) {
for (let i = 0; i < n; i++) {
try {
const ret = await fn();
if(!ret) throw new Error() // if `ret` is null or undefined, we will retry.
return ret
} catch {}
}
throw new Error(`Failed retrying ${n} times`);
}
Is there a way to tweak this function to satisfy my use cases?
Since your function is async, you can easily create timeouts to wait between subsequent calls:
const sleep = t => new Promise(r => setTimeout(r, t))
async function retry(fn) {
const startTime = Date.now()
while(Date.now() - startTime < 5 * 60 * 1000) {
try {
const ret = await fn();
if(ret)
return ret
// if `ret` is null or undefined, we won't return.
} catch {}
await sleep(10 * 1000)
}
throw new Error(`Failed retrying`);
}

Repeat sending the request to another server until the expected result is achieved

I'm requesting to server "S" to get some data, but this data may not be ready.
When the data is not yet ready, server S responds with {"data":null,"state": "pending"} but when the data has been prepared the response will be something like {"data": {...somedata}, "state": "done"}.
I have to repeat the request until the data is ready. What I'm doing now is something like this:
let wait = function* () {
let t = 500;
for (let j = 1; j < 10; j++) {
yield new Promise((resolve) => {
setTimeout(() => resolve(), t*=2);
});
}
}();
let result = await sendRequestToS();
status = result;
for (let i = 0; i < 4 && result.state==='pending'; i++) {
await wait.next().value;
result = await sendRequestToS();
}
As you can see, I send the request up to 4 times with a delay of 1, 2, 4 and 8 seconds.
Am I doing this the right way?
Isn't that (using setTimeout to delay between requests) a bad practice?
I'd write this as such:
function wait(ms) {
return new Promise(res => setTimeout(res, ms));
}
async function requestAndRetry() {
let retries = 10;
let timeout = 1000;
while(retries>0) {
const response = await sendRequestToS();
if (result?.state === 'done') {
return result;
}
await wait(timeout);
retries--;
timeout*=2;
}
throw new Error('Request failed after 10 retries');
}
I don't think it's a bad idea. It's called exponential back-off and you're not blocking the script engine.
Instead of using generators directly, you could simply do this using async/await and recursion. Here's an example which tries to get the response a limited number of times in order to prevent an endless recursion and with a timeout between retries:
async function wait(timeInMs) {
console.log('Waiting ...');
return new Promise((resolve => setTimeout(() => resolve(), timeInMs)));
}
async function tryRequestToS(numberOfTries, timeout) {
if (numberOfTries <= 0) {
throw new Error("could net get result");
}
const result = await sendRequestToS();
if (result && result.state === "done") {
return result;
}
await wait(timeout); // wait for the defined timeout before recurring
return tryRequestToS(numberOfTries - 1, timeout);
}
(async () => {
try {
const result = await tryRequestToS(10, 500); // max. 10 retries, 500 ms delay between retries
console.log(result);
} catch(err) {
console.log(err);
}
})();

JavaScript promise won't resolve or reject, it just blocks for some reason

I'm writing a node script, which is supposed to order images by a score value, calculated by a function called getImageScore(). This score takes quite some time to be calculated, therefore I created a promise (promiseImageScore) that would return the score value. After getting the promised value by the promiseImageScore promise, the program would log the name of the image and its score. Despite creating a promise for the score values, they still come back as undefined. I observed the promises would never finish, they just always remain in pending state. I tried tracing what's actually happening in the getImageScore() function, by logging some messages through the function and, as expected, those logs stop at some point, but I cannot get why this happens. Here is the full program:
const fs = require('fs');
const { resolve } = require('path');
const { reject } = require('q');
const { Console } = require('console');
const gm = require('gm').subClass({imageMagick: true});
const PNG = require("pngjs").PNG;
let pathToFolder = '/home/eugen/Pictures/wallpapers1';
let pathToImage = '';
let promiseImageScore = new Promise((resolve, reject) => {
resolve(getImageScore());
});
function getImageScore() {
console.log('entered this promise....');
let img = gm(pathToImage);
// Get the PNG buffer
img.toBuffer("PNG", (err, buff) => {
if (err) return new Error(err);
console.log('got buffer...');
// Get the image size
img.size((err, size) => {
if (err) {
console.log(err);
return new Error(err);
}
console.log('got image size...');
// Parse the PNG buffer
let str = new PNG();
str.end(buff);
// After it's parsed...
str.on("parsed", buffer => {
// Get the pixels from the image
let idx, score = 0, rgb = {r: 0, g: 0, b: 0};
for (let y = 0; y < size.height; y++)
for (let x = 0; x < size.width; x++) {
idx = (size.width * y + x) << 2;
rgb.r = buffer[idx];
rgb.g = buffer[idx + 1];
rgb.b = buffer[idx + 2];
score += (rgb.r + rgb.g + rgb.b) / 765;
}
console.log('one promised finished...');
return score / (size.height * size.width);
});
str.on("error", e => {
return new Error(e);
});
});
});
}
// see which images are to be found in the specificd directory
fs.readdir(pathToFolder, function (err, files) {
if (err) return console.log('Unable to scan directory: ' + err);
console.log('files in directory:\n');
files.forEach(function (file) {
pathToImage = pathToFolder + '/' + file;
//showImageScore();
promiseImageScore
.then(imageScore => {
console.log(file + ' has a score of ' + imageScore);
})
.catch(e => {
throw e;
})
});
});
Here is the output of the code:
entered this promise....
files in directory:
Boats_Thailand_Sea_Crag_Nature_8000x5224.jpg has a score of undefined
Wallpaper_8K_0_7680x4320.jpg has a score of undefined
Water_mountains_landscapes_nature_snow_valley_rocks_switzerland_rivers_3840x2400.jpg has a score of undefined
Waterfalls_USA_Crag_Trees_Hocking_Hills_State_Park_Ohio_Nature_10929x5553.jpg has a score of undefined
cats_blue_eyes_animals_pets_4288x2848.jpg has a score of undefined
cats_blue_eyes_animals_pets_4288x2848.png has a score of undefined
city_night_panorama_117682_3840x2160.jpg has a score of undefined
starry_sky_tree_night_sky_119989_1920x1080.jpg has a score of undefined
got buffer...
After the got buffer... log, the program keeps running, never stopping, apparently doing nothing.
Your code is not working with promises correctly. You need to make several changes:
Pass the resolve and reject to getImageScore:
const promiseImageScore = new Promise((resolve, reject) => {
// You need to pass resolve and reject to getImageScore
getImageScore(resolve, reject);
});
Use resolve and reject in getImageScore:
// For example
img.toBuffer("PNG", (err, buff) => {
// If you get an error you need to call reject and return from the function
// You will need to do the same for img.size((err, size) => {})
// and for str.on("error", e => {})
if (err) return reject(err);
// Then when you have your result and there were no errors,
// you will need to call resolve with the result
resolve(score / (size.height * size.width));
});

Recursion with Promises means registered callbacks get called many times

So my thinking was right, when recursing with promises, we end up calling all chained callbacks for however many times we recurse, for example
function p() {
return new Promise(function (r) {
process.nextTick(r);
})
}
function recurse(count) {
return p().then(function () {
if (count < 10) {
console.log('count => ', count);
return recurse(++count);
}
}).then(function(){
console.log('a');
return 5;
});
}
recurse(1).then(function () {
console.log('done');
});
If you run the above, we get:
count => 1
count => 2
count => 3
count => 4
count => 5
count => 6
count => 7
count => 8
count => 9
a
a
a
a
a
a
a
a
a
a
done
Is there a way to register this callback with console.log('a') just once instead of registering it 10 times?
I don't think I want/need this function to be called 10 times, and would like to find a way to have it called just once.
I am actually just as interested in a similar solution for Observables, specifically RxJS5 Observables.
I guess the only solution is to nest the remainder of your code inside the first promise callback like so:
function p() {
return new Promise(function (r) {
process.nextTick(r);
})
}
function recurse(count) {
return p().then(function () {
if (count < 10) {
return recurse(++count);
} else {
// all your remaining code would have to go here
console.log('a');
return 5; // or return someOtherPromise() or anything
}
});
}
recurse(1).then(function () {
console.log('done');
});
If the recursion is synchronous, you can simply recurse within the .then's function
new Promise(res => {
res(); // dummy initial promise
}).then(() => {
function recurse(x) { // recursion inside then
console.log('x', x);
if (x < 10) return recurse(++x);
return x;
}
return recurse(1); // begin recursion
}).then(y => { // this only fires once recursion above is resolved
console.log('y', y);
return 5;
}).then(z => console.log('z', z));
// x 1
// ... (synchronous)
// x 10
// y 10 (value passed from resolving inner promise)
// z 5 (value returned from previous then)
Or if our recursive function is asynchronous, we can have it return a promise too, so you end up with a recursion which looks like this
function doWork() {
function addOne(x) {
return new Promise((res, rej) => {
// async bit here
res(x + 1);
});
}
function recurse(x) {
if (x < 10) return addOne(x).then(recurse);
return x;
}
return recurse(1);
}
And in a promise chain, would look like this
new Promise(res => {
res(); // dummy initial promise
}).then(() => {
return doWork();
}).then(y => {
console.log(y);
});

Categories

Resources