check out my latest app: Icon Preview
and my latest blog post
Retry a promise-returning or async function
License: MIT License
check out my latest app: Icon Preview
and my latest blog post
I am using version 2.0.0 , What is the breaking changes on version 3.0.0?
Could we please backport #68 to p-retry 4.x to support retries of undici network errors?
I would want it to keep trying when a NetworkError
is thrown, but if it's a PermissionError
it should abort and re-throw the exception.
When we give retries
Infinity value, it will produce this error
#
# Fatal error in , line 0
# Fatal JavaScript invalid size error 169220804
#
#
#
#FailureMessage Object: 0000007FAFAFD6C0
1: 00007FF6B5CC30AF v8::internal::CodeObjectRegistry::~CodeObjectRegistry+112511
2: 00007FF6B5BE023F v8::CFunctionInfo::HasOptions+7055
3: 00007FF6B68B6392 V8_Fatal+162
4: 00007FF6B6440153 v8::internal::FactoryBase<v8::internal::Factory>::NewFixedDoubleArray+259
5: 00007FF6B62E8B73 v8::internal::FeedbackNexus::ic_state+60339
6: 00007FF6B62FFBD0 v8::Message::GetIsolate+14688
7: 00007FF6B6174711 v8::internal::CompilationCache::IsEnabledScriptAndEval+26849
8: 00007FF6B6612541 v8::internal::SetupIsolateDelegate::SetupHeap+494417
9: 000001FE08B399D9
Work around:
await pRetry(run, {
retries: Infinity,
onFailedAttempt: () => {
console.log("Retrying..");
},
});
"p-retry": "^5.0.0"
Hi,
Would it be possible to wait until a promise is resolve (or rejected) inside the onFailedAttempt handler before throwing an AbortError? If so, how?
My use case is as follow : I'm fetching data from an API protected by a token, and I'd like to be able to retry the fetch if the token is invalid, but before retrying, I need to try refreshing the token.
Thanks for your help!
Is it possible to merge the AbortController feature in 4.x? Unfortunately, ESM is not supported everywhere (e.g. Jest supports it but with a lot of caveats -- mocks are not working for example) and the cancellation feature is quite useful.
I am happy to open a PR but there's no branch for 4.x.
Function call
const response = await pRetry(() => { fetchbulkEditorItems(internalParams)}, {
retries: API_RETRY
});
Function
export const fetchbulkEditorItems = async (internalParams) => {
const url = `${bulkEditorUrl}&${qs.stringify(internalParams)}`;
const response = await fetch(url, {
headers: apiHeaders
});
return response;
};
Hi,
It would be great if you could use babel to transpile ES6 upon npm publish. Currently, I can't utilize your package when using webpack + UglifyJs without having to explicitly transpile your package, being a node_module. I believe it's a common practice for this to be done for any public npm package.
Thanks!
How can I even use this library in node? I cannot even import it!
Steps to reproduce:
mkdir pretry && cd pretry
yarn init -y
yarn install p-retry
echo 'const pRetry = require("p-retry")' >> main.js
node main.js
Expected result: Actually nothing, the script does nothing
Actual result:
node:internal/modules/cjs/loader:1112
throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
^
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/alberto/pretry/node_modules/p-retry/index.js
require() of ES modules is not supported.
require() of /home/alberto/pretry/node_modules/p-retry/index.js from /home/alberto/pretry/main.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /home/alberto/pretry/node_modules/p-retry/package.json.
at new NodeError (node:internal/errors:370:5)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1112:13)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
at Module.require (node:internal/modules/cjs/loader:999:19)
at require (node:internal/modules/cjs/helpers:93:18)
at Object.<anonymous> (/home/alberto/pretry/main.js:1:16)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32) {
code: 'ERR_REQUIRE_ESM'
}
This is a simplified example. Of course I do write all my code using ES modules, but then again, I write them in TS and then transpile them down to JS using "module": "commonjs"
in my tsconfig.json
, lest the transpiled output will not run in node.
I don't know whether I am doing something very wrong or I simply cannot use this library in node. Any tips?
Other information:
$ node -v
v16.5.0
$ yarn -v
1.22.17
i'm getting back the attempt count instead of the input promise's resolved value.
is this expected behavior?
const result = await pRetry(myPromise(data), { retries: 5 })
console.log(result) // 1
Hello,
I know you no longer currently support Node v4.x.x, but would you please point me to the most recent release version which supports Node v4.x.x?
(Out of curiosity, which features have you added that break compatibility with Node v4.x.x, but work with Node version 6?)
Thank you!
In the README.md, the following code exists:
const pRetry = require('p-retry');
const delay = require('delay');
const run = async () => { ... };
(async () => {
const result = await pRetry(run, {
onFailedAttempt: async error => {
console.log('Waiting for 1 second before retrying');
await delay(1000);
}
});
})();
Without knowing the retry module's features, a user may expect that this code would retry the run
function every 1 second. This is not accurate as the retry module has an exponential delay function where, by default, it begins with a 1 second delay in between the first two tries and then multiplies 1 second by 2^(number of tries). The code provided then waits a single second before the retry module's delay between tries executes.
Ideally, this example should be removed since it could lead to confusion for new users of the library.
Is there any reason a retry loop can't be nested inside another retry loop?
thanks
Is there any breaking changes on version 4.0.0 , I am using 3.0.0
See: #67
This would allow adding a delay before retry
I'm rethrowing an error inside my "run" function and it's not being caught by p-retry
.
Example:
app.js
import retry from 'p-retry'
import fetch200 from './fetch200'
const resultResponse = await retry(
() => { fetch200(url) },
{
retries: 10,
maxRetryTime: 10 * 60 * 1000, // 10 minutes
onFailedAttempt: (err => console.log('one failed attempt with error: %o', err))
}
)
fetch200.js
export default async function fetch200() {
const resp = await fetch.apply(this, arguments)
if (resp.status < 200 || resp.status >= 300) {
throw new Error(
`invalid fetch response status ${resp.status}: ${resp.statusText}`
)
}
return resp
}
I've also tried async () => { fetch200(url) }
instead of () => { fetch200(url) }
to see if this is why the exception is not bubbling up, but this doesn't seem to be the case.
Actual behavior:
While trying to import using node's require-syntax, you will get the following error:
Error [ERR_REQUIRE_ESM]: require() of ES Module /node_modules/p-retry/index.js from /test.js not supported.
Instead change the require of index.js in /test.js to a dynamic import() which is available in all CommonJS modules.
at Object.<anonymous> (/test.js:1:14) {
code: 'ERR_REQUIRE_ESM'
}
As my whole application is using requires and isn't loaded with imports, it would be great if you provide an option to import your module using require.
Sometimes you just want to cancel a retry operation:
const abortController = new AbortController();
const {signal} = abortController;
button.addEventListener('click', abortController.abort, {signal})
await pRetry(run, {signal}));
Getting this error whenever I try to run my app:
Package p-retry has been ignored because it contains invalid configuration
I've tried yarn remove p-retry
then re-adding it but that didn't seem to work either. I noticed that the package is missing from my node_modules when to search for it in my directory however alt-clicking directs me to a file that is supposedly in the node_modules
directory. I can find it in my file explorer but not in intellij basically.
I dont see a way to stop the retry on SIGINT? Right now we throw an exception throw new pRetry.AbortError('');
which isn't clean but something like operation.stop
from the underlying library should help. Is this possible?
I'm using cross-fetch in a number of projects since it allows polymorphic code across react-native, node and browser. The library has quite a bit of traction at this point (in use by 301K projects as of this writing according to github).
Currently this library doesn't support the network error text that is emitted by cross-fetch
when a network error occurs. So it treats it as a general error and does not retry.
https://github.com/lquixada/cross-fetch/blob/main/dist/browser-ponyfill.js#L480
Would be great if p-retry could include this error into the list of networkErrorMsgs
Currently, as the API states, you discard instance of TypeError
, however, the fetchApi Failed to fetch
exception is of type TypeError
.
One of the common reasons that this error occurs can be network connection issues.
Is it possible to include this exception?
The main reason I want to add a retry mechanism is incase the user had a bad internet connection during that second where the request was going out
From Fetch documentation:
A fetch() promise will reject with a TypeError when a network error is encountered or CORS is misconfigured on the server-side
Current workaround:
pRetry(async () => {
try {
return await fetchRequest()
} catch (error) {
if (error instanceof TypeError && error.message === 'Failed to fetch') {
throw new Error('Failed to fetch')
}
throw error;
}
}, {
retries: 2,
onFailedAttempt,
});
Thanks
Any example of CommonJS import for 5.0.0 would be appreciated. Migrating to ES6 imports is not something everyone can afford at the moment due to the inability to use both CommonJS and ES6 imports in the same project.
import('p-retry') fails.
Based on this issue, p-retry now can be used to retry based on network errors on Chrome. Nice!
However, as mentioned in the 'slightly relevant ' fetch issue, different browsers have different messages based on the network errors:
Chrome: TypeError: Failed to fetch
Firefox: TypeError: NetworkError when attempting to fetch resource.
Safari: TypeError: The Internet connection appears to be offline.
(source)
Would it be possible to support network errors in Firefox and Safari, and also retry on them -- also happy to put in a PR by referencing https://github.com/sindresorhus/p-retry/pull/38/files.
“Trying again in 32 seconds.”
I propose p-retry
provide error.timeout_duration
, that is, in how much time the next retry begins, for onFailedAttempt
.
A default is provided for the onFailedAttempt
option, but only if onFailedAttempt
is not in the options
object Relevant code:
Lines 31 to 35 in 0608b64
This means that if options.onFailedAttempt
is null
or undefined
, you get a TypeError
when attempting to invoke it. But in passing null
or undefined
, the caller probably wanted to use the default onFailedAttempt
handler (I know that's what I want).
This is obviously easy to workaround once you know about it, but does seem like a gotcha.
The abort
event listener is currently not removed when the task finished (without any abort). It leads to the following warning:
(node:1852) MaxListenersExceededWarning: Possible EventTarget memory leak detected.
71 abort listeners added to [AbortSignal]. Use events.setMaxListeners() to increase limit
As a best practice, the one who added the event listener to also be responsible for removing it.
Function decorators could be used like so:
const pRetry = require('p-retry');
class Example {
@pRetry.Decorator()
static async retriableFunction() {
//...
}
}
Hi!
Recently, when attempting to import the library using CommonJS as follows:
const pRetry = require('p-retry');
I received the following error:
Detailed stack trace: Error [ERR_REQUIRE_ESM]: require() of ES Module <stack-files> not supported. Instead change the require of index.js in <stack-files> to a dynamic import() which is available in all CommonJS modules.
While researching the problem, I found a possible solution in this Stack Overflow post. The solution involves making some additional configurations to add CommonJS support to the library. This could significantly enhance the library's experience for developers who use JavaScript and CommonJS.
Thanks
If the input
function somehow rejects with a non-object, decorateErrorWithCounts
will throw an error which leads to an unhandled rejection.
Example:
const pRetry = require('./');
pRetry(() => Promise.reject(Object.preventExtensions(new Error())))
.catch(console.error);
Expected result:
The retry handler treats the non-extendable like any other (minus the decorating of course).
Actual result:
An UnhandledPromiseRejectionWarning
is generated, the code continues on neither the resolution nor the rejection path.
Generally the code seems to not be well defended against errors from unexpected sources. For example. throwing from the onFailedAttempt
function would also result in an unhandled rejection.
API for pRetry
states:
Returns a Promise that is fulfilled when calling input returns a fulfilled promise. If calling input returns a rejected promise, input is called again until the max retries are reached, it then rejects with the last rejection reason.
It doesn't retry on TypeError as that's a user error.
I am passing a rejected Promise to pRetry
which then raises a UnhandledPromiseRejectionWarning: TypeError
due to undefined objects keys of the raised error. According to the error message logged on console, the function decorateErrorWithCounts
tries to access attemptNumber
and retriesLeft
which are not defined for the rejection message the rejected Promise is rejected with. Since the docs address a TypeError
as an user error explicitly, I am not sure wether this is a problem with the (missing) error handling on my side or inside p-retry
.
Full traceback is as follows:
$ node playground.js
(node:21184) UnhandledPromiseRejectionWarning: TypeError: Cannot create property 'attemptNumber' on string 'rejected the promise'
at decorateErrorWithCounts (/<projectDirectory>/node_modules/p-retry/index.js:25:22)
at Promise.resolve.then.then.error (/<projectDirectory>/node_modules/p-retry/index.js:49:5)
(node:21184) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:21184) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:21184) UnhandledPromiseRejectionWarning: TypeError: Cannot create property 'attemptNumber' on string 'rejected the promise'
at decorateErrorWithCounts (/<projectDirectory>/node_modules/p-retry/index.js:25:22)
at Promise.resolve.then.then.error (/<projectDirectory>/node_modules/p-retry/index.js:52:5)
(node:21184) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
Code snippet to reproduce error:
'use strict';
const pRetry = require('p-retry');
const myWrapper = (value) => {
return () => {
return new Promise( (resolve, reject) => {
console.log(value);
setTimeout(function() {
// resolve('resolved the promise');
reject('rejected the promise');
}, 1000);
})
}
}
pRetry(myWrapper(2000),
{
retries: 1,
})
.then(console.log)
.catch(console.log)
Working on Mac OS 10.10.5 with NodeJS 10.9.0 and p-retry 3.0.0.
Following the discussion on #43:
Currently, I can only opt out of retrying by throwing an AbortError
. It's not possible to opt in to retrying for the cases that p-retry
does not deem to be retry-worthy.
I'll draft a PR so we have a base for discussion.
The error I'm receiving is "TypeError: NetworkError when attempting to fetch resource."
And p-retry doesn't retry at all.
Hi,
I would like to know how can I fake the minTimeout of my r-retried function using sinon.fakeTimers or anything equivalent ?
Thanks
I could see this function being useful to check whether a given error is a fetch network error.
Getting
(node:15140) UnhandledPromiseRejectionWarning: TypeError: Cannot create property 'attemptNumber' on string '
warning.js:18
┌────────────────────────────────────────────────────────────┐
│ npm update check failed │
│ Try running with sudo or get access │
│ to the local update config store via │
│ sudo chown -R $USER:$(id -gn $USER) C:\Users\kelly\.config │
└────────────────────────────────────────────────────────────┘
'
We should instead send a new object to onFailedAttempt
.
Hi, I'm using p-queue and I would like to be able to retry a job after it throws a timeOut error. How can I do that?
Thanks
Instead of passing a function to immediately call it, I'd like to wrap a function to be used later. From:
function runMeNow() {}
function init() {
pRetry(runMeNow, {
bigOptionsObject
}).then(console.log)
}
init();
to:
const runMeRetried = pRetry.wrap(runMeNow, {
bigOptionsObject
});
function init() {
runMeRetried.then(console.log)
}
init();
This is the example code on the readme.md.
import pRetry from 'p-retry';
const run = async () => {
const response = await fetch('https://sindresorhus.com/unicorn');
if (!response.ok) {
throw new Error(response.statusText);
}
return response.json();
};
const result = await pRetry(run, {
onFailedAttempt: error => {
console.log(`Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left.`);
// 1st request => Attempt 1 failed. There are 4 retries left.
// 2nd request => Attempt 2 failed. There are 3 retries left.
// …
},
retries: 5
});
console.log(result);
The comments say
// 1st request => Attempt 1 failed. There are 4 retries left.
// 2nd request => Attempt 2 failed. There are 3 retries left.
But this is neither true nor the actual output. When the 1st attempt fails, there are still 5 retries left, because the retries
does not count the first try as a re-try.
Fortunately this issue is not a bug of the code, but a mistake on the documentation.
The actual output for the retries
of 5
is:
onFailedAttempt({attemptNumber: 1, retriesLeft: 5})
onFailedAttempt({attemptNumber: 2, retriesLeft: 4})
onFailedAttempt({attemptNumber: 6, retriesLeft: 0})
The error object returned in onFailedAttempt
returns the wrong counts for the attempts.
const retry = require('p-retry');
function test(times) {
let c = 0;
return function prom() {
let p;
if (times === c) {
p = Promise.resolve('true');
} else {
p = Promise.reject(new Error('false'));
}
c += 1;
return p;
};
}
const f = test(3);
retry(f, {
retries: 3,
maxTimeout: 0,
minTimeout: 0,
onFailedAttempt(e) {
console.log(`Attempt ${e.attemptNumber} failed. There are ${e.attemptsLeft} attempts left.`);
},
})
.then(v => {
console.log('success', v);
})
.catch(e => {
console.log('fail', e);
});
The above prints out
$ node index.js
Attempt 1 failed. There are 2 attempts left.
Attempt 2 failed. There are 1 attempts left.
Attempt 3 failed. There are 0 attempts left.
success true
Maybe I'm using it wrong but if I have some type of logging in onFailedAttempt
that checks to see if error.attemptsLeft === 0
and then logs that an operation failed. That may not be true. The very last attempt could still succeed.
A few hours ago, a new version of @types/retry was published. This changed the definition of OperationOptions
, which this package depends upon.
Whenever attempting to use p-retry, I now receive the following error:
npm ERR! ../../p-retry/index.d.ts(20,31): error TS2312: An interface can only extend an object type or intersection of object types with statically known members.
Not sure if a code change or pinning a specific version of retry
is the way to go here.
It would be nice to be able to retry async requests with backoff that grows based on the number of times the request has failed. For example here's a case where you'd delay the retry by a fixed value in the event of a 429:
const run = () => fetch('https://sindresorhus.com/unicorn')
.then((response) => {
if (response.status === 429) {
return new Promise((resolve, reject) => setTimeout(() => reject(err), 1000));
}
});
pRetry(run, { retries: 5 });
Better would be if there were some way to delay by Math.pow(2, numberOfAttempts)
or something along those lines. Maybe enabling it through an option? Perhaps the caller provides a function that calculates the retry delay?
pRetry(run, { retries: 5, delay: (num) => Math.pow(2, num) * 1000 });
While trying to use p-retry in a TypeScript project, from within a Jest test, I'm getting the following full error output:
$ npm t
> [email protected] test /Users/xxxx/xxx/jest2
> jest
FAIL ./sum.spec.ts
● Test suite failed to run
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
• If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation
Details:
/Users/xxxx/xxxx/jest2/node_modules/p-retry/index.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import retry from 'retry';
^^^^^^
SyntaxError: Cannot use import statement outside a module
> 1 | import pRetry from 'p-retry';
| ^
2 |
3 | export function sum(a: number, b: number): number {
4 | pRetry(async () => { return Promise.resolve() });
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1728:14)
at Object.<anonymous> (sum.ts:1:1)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 0.659 s
Ran all test suites.
npm ERR! Test failed. See above for more details.
It doesn't happen with other modules, like Axios, for example.
Tested with p-retry 5.1.0, Node 16.14.2, observe the error while running npm test
See attached sample project
sample.zip
it would be nice to have an onError function hooked up to https://github.com/tim-kos/node-retry#retryoperationattemptfn-timeoutops
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.