-
-
Save briancavalier/842626 to your computer and use it in GitHub Desktop.
function keepTrying(otherArgs, promise) { | |
promise = promise||new Promise(); | |
// try doing the important thing | |
if(success) { | |
promise.resolve(result); | |
} else { | |
setTimeout(function() { | |
keepTrying(otherArgs, promise); | |
}, retryInterval); | |
} | |
} |
Hey, what Promise library are you using? This doesn't appear to be EMCA 6 compatible: promise = promise||new Promise();
If you're looking for a Q solution, check this out: https://gist.github.com/kriskowal/593052
👍
@spacenick function(err){ if (maxTries == -1) return Q.error(err);
swallows the error
An alternative solution:
const retry = (fn, ms) => new Promise(resolve => {
fn()
.then(resolve)
.catch(() => {
setTimeout(() => {
console.log('retrying...');
retry(fn, ms).then(resolve);
}, ms);
})
});
Related: puppeteer/puppeteer#2460
@curran 's solution with max retries
const retry = (fn, ms=1000,maxRetries=5) => new Promise((resolve,reject) => {
var retries=0;
fn()
.then(resolve)
.catch(() => {
setTimeout(() => {
console.log('retrying failed promise...');
++retries;
if(retries==maxRetries) {
return reject('maximum retries exceeded');
}
retry(fn, ms).then(resolve);
}, ms);
})
});
@r3wt thanks!
Thanks @r3wt!
I'd modify the code a bit because I wasn't being able to catch the max retries exception. Also, we don't really need a counter:
function retry(fn, retriesLeft = 5, interval = 1000) {
return new Promise((resolve, reject) => {
fn()
.then(resolve)
.catch((error) => {
setTimeout(() => {
if (retriesLeft === 1) {
// reject('maximum retries exceeded');
reject(error);
return;
}
// Passing on "reject" is the important part
retry(fn, interval, retriesLeft - 1).then(resolve, reject);
}, interval);
});
});
}
Heres an ES6, async/await version. Thank you guys! https://gitlab.com/snippets/1775781
@eliseumds If case you ever needed to debug it, you (I reckon accidentally) flipped arguments to retry
there retry(fn, interval, retriesLeft - 1).then(resolve, reject);
, it should be retry(fn, retriesLeft - 1, interval).then(resolve, reject);
if (retriesLeft === 1) {
// reject('maximum retries exceeded');
return reject(error);
}
I think that should be outside setTimeout
In case it's useful to anyone, here is a published package with all fixes mentioned above included, and tests added.
your recursive call parameters seems incorrect,
function retry(fn, retriesLeft = 5, interval = 1000) {
// ...code...
retry(fn, retriesLeft - 1, interval).then(resolve, reject);
// retry(fn, interval, retriesLeft - 1).then(resolve, reject);
Finally version:
export function retryPromise(fn, retriesLeft = 3, interval = 200) {
return new Promise((resolve, reject) => {
return fn()
.then(resolve)
.catch((error) => {
if (retriesLeft === 1) {
// reject('maximum retries exceeded');
reject(error)
return
}
setTimeout(() => {
console.log('retriesLeft: ', retriesLeft)
// Passing on "reject" is the important part
retry(fn, retriesLeft - 1, interval).then(resolve, reject)
}, interval)
})
})
}
const wait = interval => new Promise(resolve => setTimeout(resolve, interval));
async function retryPromise(fn, retriesLeft = 3, interval = 200) {
try {
return await fn;
} catch (error) {
await wait(interval);
if (retriesLeft === 0) {
throw new Error(error);
}
console.log('retriesLeft: ', retriesLeft);
return await retryPromise(fn, --retriesLeft, interval);
};
}
retryPromise(Promise.reject('Maximum retries exceeded!'), 5, 1000);
@cristianb
You're missing () in here return await fn;
we need it to run the imported component, it should be return await fn();
So final code is
const wait = interval => new Promise(resolve => setTimeout(resolve, interval));
export async function retry(fn, retriesLeft = 3, interval = 200) {
try {
return await fn();
} catch (error) {
await wait(interval);
if (retriesLeft === 0) {
throw new Error(error);
}
return await retry(fn, --retriesLeft, interval);
}
}
In case we want to pass parameters with the retry function like this
const wait = interval => new Promise(resolve => setTimeout(resolve, interval));
async function retry(
fn,
args = [],
retriesLeft = 3,
interval = 300,
) {
try {
return await fn(...args);
} catch (error) {
await wait(interval);
if (retriesLeft === 0) {
throw new Error(error);
}
return retry(fn, args, --retriesLeft, interval);
}
}
// uses
const promiseFn1 = function(val) {
return new Promise( (resolutionFunc) => {
resolutionFunc(val);
});
}
await retry(promiseFn1, [88, 2]);
For example:
async function myFun () {
const wait = interval => new Promise(resolve => setTimeout(resolve, interval));
async function retry(
fn,
args=[],
retriesLeft = 3,
interval = 300,
) {
try {
return await fn(...args);
} catch (error) {
await wait(interval);
if (retriesLeft === 0) {
throw new Error(error);
}
return retry(fn, args, --retriesLeft, interval);
}
}
// uses
const promiseFn1 = function(val) {
return new Promise((resolve, reject) => {
console.log('called');
if (val) return resolve(val)
return reject('This is reject val');
});
}
const result = await retry(promiseFn1);
console.log('result', result);
}
myFun();
@bablukpik any idea how this would work with Batch operations? Like using promise.All or promise.AllSettled?
my try:
const retry = (fn, delay = 1000, maxTries = 5) => {
let timer
const attempt = remain => {
clearTimeout(timer)
return new Promise((resolve, reject) => {
timer = setTimeout(() => {
if (remain <= 0)
return reject(new Error(`Failed after ${maxTries} attempts`))
resolve(attempt(remain - 1))
}, delay)
Promise.resolve(fn())
.then(res => {
clearTimeout(timer)
resolve(res)
})
.catch(error => {
clearTimeout(timer)
if (remain <= 0)
return reject(new Error(`Failed after ${maxTries} attempts - Error: ${error.toString()}`))
resolve(attempt(remain - 1))
})
})
}
return attempt(maxTries)
}
Based on previous examples, I have also added an internal multiplication function that based on the last interval sets a new internal.
For instance with doubling intervals and 3 retries.
- First retry after 1000 ms
- Second retry after 2000 ms
- Third retry after 4000 ms
Typescript example:
export const retry = (fn: Function, retriesLeft = 5, interval = 1000, intervalMultiplier: (interval: number) => number = i => i) => new Promise((resolve, reject) => {
console.log(`Retries left: ${retriesLeft} - Next retry interval: ${interval}`);
fn()
.then(resolve)
.catch((error: unknown) => {
if (retriesLeft === 0) {
// reject('maximum retries exceeded');
reject(error);
return;
}
setTimeout(() => {
// Passing on "reject" is the important part
retry(fn, retriesLeft - 1, intervalMultiplier(interval), intervalMultiplier).then(resolve, reject);
}, interval);
});
});
// example function
const chanceOfFailing = () : Promise<string> => {
return new Promise<string>((resolve, reject) => {
if(Math.random() < 0.1) {
resolve("success");
} else {
reject("fail");
}
});
}
// example usage
retry(chanceOfFailing, 3, 1000, i => i*2).then((result) => {
console.log(result);
}).catch((error) => {
console.log(error);
})
Hey thanks for sharing, if that can help I'm using this (requires Q though)
And you'd use it like that :