Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
ts-retry-promise
Advanced tools
The ts-retry-promise npm package provides a way to retry promises with customizable options. It is useful for handling transient errors in asynchronous operations by retrying the operation until it succeeds or a maximum number of retries is reached.
Basic Retry
This feature allows you to retry a promise-based function a specified number of times. In this example, the fetchData function simulates an API call that may fail. The retry function will attempt to call fetchData up to 3 times before giving up.
const retry = require('ts-retry-promise');
const fetchData = () => {
return new Promise((resolve, reject) => {
// Simulate an API call that may fail
if (Math.random() > 0.5) {
resolve('Data fetched successfully');
} else {
reject('Fetch failed');
}
});
};
retry(fetchData, { retries: 3 })
.then(result => console.log(result))
.catch(err => console.error(err));
Exponential Backoff
This feature allows you to retry a promise-based function with exponential backoff. The delay between retries increases exponentially. In this example, the initial delay is 1000ms and it doubles with each retry.
const retry = require('ts-retry-promise');
const fetchData = () => {
return new Promise((resolve, reject) => {
// Simulate an API call that may fail
if (Math.random() > 0.5) {
resolve('Data fetched successfully');
} else {
reject('Fetch failed');
}
});
};
retry(fetchData, { retries: 3, delay: 1000, factor: 2 })
.then(result => console.log(result))
.catch(err => console.error(err));
Custom Retry Logic
This feature allows you to define custom logic to determine if a promise should be retried. In this example, the shouldRetry function checks if the error message is 'Fetch failed' to decide whether to retry the fetchData function.
const retry = require('ts-retry-promise');
const fetchData = () => {
return new Promise((resolve, reject) => {
// Simulate an API call that may fail
if (Math.random() > 0.5) {
resolve('Data fetched successfully');
} else {
reject('Fetch failed');
}
});
};
const shouldRetry = (err) => {
// Custom logic to determine if the error is retryable
return err === 'Fetch failed';
};
retry(fetchData, { retries: 3, shouldRetry })
.then(result => console.log(result))
.catch(err => console.error(err));
The promise-retry package provides similar functionality for retrying promises. It allows for customizable retry strategies, including exponential backoff. Compared to ts-retry-promise, promise-retry offers a more flexible API for defining retry strategies.
The async-retry package is another alternative for retrying asynchronous operations. It supports various retry strategies and is highly configurable. It is similar to ts-retry-promise but offers additional features like custom retry strategies and more detailed error handling.
The retry package provides a comprehensive set of tools for retrying operations. It supports both synchronous and asynchronous retries, with customizable options for delay, retries, and backoff strategies. It is more feature-rich compared to ts-retry-promise, making it suitable for more complex retry scenarios.
retry for functions returning a promise
Install with yarn:
yarn add ts-retry-promise
Install with npm:
npm install --save ts-retry-promise
Then you can import it with:
import { retry } from 'ts-retry-promise';
const result: number = await retry(() => Promise.resolve(1), {retries: 3});
This will instantly start calling your function until it returns a resolved promise, no retries are left or a timeout occurred.
If you want to add retries to an existing function, use the decorator:
import { retryDecorator } from 'ts-retry-promise';
const asyncFunction = async (s: String) => s;
const decoratedFunction = retryDecorator(asyncFunction, {timeout: 1});
const result: string = await decoratedFunction("1");
Here decoratedFunction
is a function with the same signature as asyncFunction
, but will do retries in case of failures.
Both retry
and retryDecorator
take an optional second argument where you can configure the number of retries and timeouts:
export interface RetryConfig<T> {
// number of maximal retry attempts (default: 10)
retries?: number | "INFINITELY";
// wait time between retries in ms (default: 100)
delay?: number;
// check the result, will retry until true (default: () => true)
until?: (t: T) => boolean;
// log events (default: () => undefined)
logger?: (msg: string) => void;
// overall timeout in ms (default: 60 * 1000)
timeout?: number | "INFINITELY";
// increase delay with every retry (default: "FIXED")
backoff?: "FIXED" | "EXPONENTIAL" | "LINEAR" | ((attempt: number, delay: number) => number);
// maximal backoff in ms (default: 5 * 60 * 1000)
maxBackOff?: number;
// allows to abort retrying for certain errors, will retry until false (default: () => true)
retryIf: (error: any) => boolean
}
customizeRetry returns a new instance of retry that has the defined default configuration.
import { customizeRetry } from 'ts-retry-promise';
const impatientRetry = customizeRetry({timeout: 5});
await expect(impatientRetry(async () => wait(10))).to.be.rejectedWith("Timeout");
// another example
const retryUntilNotEmpty = customizeRetry({until: (array: any[]) => array.length > 0});
const result = await retryUntilNotEmpty(async () => [1, 2]);
expect(result).to.deep.eq([1, 2]);
You can do the same for decorators:
import { customizeDecorator } from 'ts-retry-promise';
const asyncFunction = async (s: string) => {
await wait(3);
return s;
};
const impatientDecorator = customizeDecorator({timeout: 1});
expect(impatientDecorator(asyncFunction)("1")).to.be.rejectedWith("Timeout");
In case retry
failed, an error is thrown.
You can access the error that occurred the last time the function has been retried via the property lastError
:
retry(async () => throw "1")
.catch(err => console.log(err.lastError)); // will print "1"
Wrapped function can throw NotRetryableError
if retrying need to be stopped eventually:
import { NotRetryableError } from 'ts-retry-promise';
retry(async () => { throw new NotRetryableError("This error") }, { retries: 'INFINITELY' })
.catch(err => console.log(err.lastError.message)); // will print "This error"
retryDecorator can be used on any function that returns a promise
const loadUserProfile: (id: number) => Promise<{ name: string }> = async id => ({name: "Mr " + id});
const robustProfileLoader = retryDecorator(loadUserProfile, {retries: 2});
const profile = await robustProfileLoader(123);
retry is well suited for acceptance tests (but not restricted to)
// ts-retry-promise/test/retry-promise.demo.test.ts
it("will retry until no exception or limit reached", async () => {
await retry(async () => {
const title = await browser.$("h1");
expect(title).to.eq("Loaded");
});
});
it("can return a result", async () => {
const pageTitle = await retry(async () => {
const title = await browser.$("h1");
expect(title).to.be.not.empty;
return title;
});
// do some stuff with the result
expect(pageTitle).to.eq("Loaded");
});
it("can be configured and has defaults", async () => {
await retry(async () => {
// your code
}, {backoff: "LINEAR", retries: 100});
});
it("will retry until condition is met or limit reached", async () => {
await retry(
() => browser.$$("ul"),
{until: (list) => list.length === 2});
});
it("can have a timeout", async () => {
const promise = retry(
() => wait(100),
{timeout: 10},
);
await expect(promise).to.be.rejectedWith("Timeout");
});
it("can create a customized retry", async () => {
const impatientRetry = customizeRetry({timeout: 5});
await expect(impatientRetry(async () => wait(10))).to.be.rejectedWith("Timeout");
});
it("can create another customized retry", async () => {
const retryUntilNotEmpty = customizeRetry({until: (array: number[]) => array.length > 0});
const result = await retryUntilNotEmpty(async () => [1, 2]);
expect(result).to.deep.eq([1, 2]);
});
it("can customize default config", async () => {
const originalTimeout = defaultRetryConfig.timeout;
try {
defaultRetryConfig.timeout = 1;
await expect(retry(async () => wait(10))).to.be.rejectedWith("Timeout");
} finally {
defaultRetryConfig.timeout = originalTimeout;
}
});
Release automation has been setup according this guide.
0.6.1
.latest
tag.FAQs
retry for functions returning a promise
We found that ts-retry-promise demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.