Merge branch 'exponential-backoff' into sse-and-backoff
This commit is contained in:
Generated
+7
-7
@@ -7715,9 +7715,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.16",
|
"version": "3.3.17",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz",
|
||||||
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
|
"integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -8197,9 +8197,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.25",
|
"version": "8.5.26",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
|
||||||
"integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==",
|
"integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -8217,7 +8217,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.16",
|
"nanoid": "^3.3.17",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -263,7 +263,32 @@ export class ExponentialBackoff {
|
|||||||
|
|
||||||
// Wait before going to the next attempt
|
// Wait before going to the next attempt
|
||||||
const delay = this.#calculateDelay(this.#options, attempt);
|
const delay = this.#calculateDelay(this.#options, attempt);
|
||||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
||||||
|
// Wait for the delay or the abort signal
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
// Set a timeout to resolve the promise after the delay
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
let timeout: ReturnType<typeof setTimeout>;
|
||||||
|
|
||||||
|
// Handle the abort signal
|
||||||
|
const abortHandler = (): void => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
abortController.signal.removeEventListener('abort', abortHandler);
|
||||||
|
reject(new ExponentialBackoffStoppedRetriesError(abortController.signal.reason));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle the timeout
|
||||||
|
const timeoutHandler = (): void => {
|
||||||
|
abortController.signal.removeEventListener('abort', abortHandler);
|
||||||
|
resolve(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the timeout
|
||||||
|
timeout = setTimeout(timeoutHandler, delay);
|
||||||
|
|
||||||
|
// Add the abort handler to the abort signal
|
||||||
|
abortController.signal.addEventListener('abort', abortHandler);
|
||||||
|
});
|
||||||
|
|
||||||
attempt++;
|
attempt++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,6 +299,48 @@ const testExponentialBackoffRunAbortedStringCreatesError = async (): Promise<voi
|
|||||||
expect(abortAndThrowStringFn).not.toHaveResolved();
|
expect(abortAndThrowStringFn).not.toHaveResolved();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that the delay is aborted when the abort signal is activated.
|
||||||
|
*/
|
||||||
|
const testExponentialBackoffRunDelayAbortedWhenAbortSignal = async (): Promise<void> => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
|
||||||
|
try {
|
||||||
|
let abort: (reason: unknown) => void;
|
||||||
|
|
||||||
|
const taskFn = vi.fn(async ({ stopRetries }) => {
|
||||||
|
abort = stopRetries;
|
||||||
|
throw new Error('error message');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the exponential backoff and wait for it to complete
|
||||||
|
const result = ExponentialBackoff.run(taskFn, () => {}, {
|
||||||
|
baseDelay: 1000,
|
||||||
|
jitter: 0,
|
||||||
|
maxAttempts: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Advance the timer by 500 (mid delay)
|
||||||
|
await vi.advanceTimersByTimeAsync(500);
|
||||||
|
|
||||||
|
// Make sure the abort function is defined (That the taskFn was called)
|
||||||
|
if (!abort!) {
|
||||||
|
throw new Error('abort is not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the abort function is defined
|
||||||
|
expect(abort).toBeDefined();
|
||||||
|
|
||||||
|
// Abort the exponential backoff
|
||||||
|
abort?.(new Error('exponential backoff aborted'));
|
||||||
|
|
||||||
|
// Expect the result to be rejected with an ExponentialBackoffStoppedRetriesError
|
||||||
|
await expect(result).rejects.toThrow(ExponentialBackoffStoppedRetriesError);
|
||||||
|
} finally {
|
||||||
|
vi.useRealTimers();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the {@link ExponentialBackoff.from} factory and subsequent instance {@link ExponentialBackoff.run}
|
* Tests the {@link ExponentialBackoff.from} factory and subsequent instance {@link ExponentialBackoff.run}
|
||||||
* as an alternative to the static helper.
|
* as an alternative to the static helper.
|
||||||
@@ -586,7 +628,6 @@ const testExponentialBackoffValidateOptionsRejectsNaN = (): void => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** Tests that passing undefined into the constructor does not cause an error during spread */
|
/** Tests that passing undefined into the constructor does not cause an error during spread */
|
||||||
const testExponentialBackoffConstructorDoesNotCauseErrorDuringSpread = async (): Promise<void> => {
|
const testExponentialBackoffConstructorDoesNotCauseErrorDuringSpread = async (): Promise<void> => {
|
||||||
const options = {
|
const options = {
|
||||||
@@ -617,6 +658,7 @@ const runTests = async (): Promise<void> => {
|
|||||||
test('ExponentialBackoff: succeeds and aborts with abort signal', testExponentialBackoffRunSuccessAndAbortSignal);
|
test('ExponentialBackoff: succeeds and aborts with abort signal', testExponentialBackoffRunSuccessAndAbortSignal);
|
||||||
test('ExponentialBackoff: aborts with abort signal', testExponentialBackoffRunWithAbortSignal);
|
test('ExponentialBackoff: aborts with abort signal', testExponentialBackoffRunWithAbortSignal);
|
||||||
test('ExponentialBackoff: aborts with aborted string creates error', testExponentialBackoffRunAbortedStringCreatesError);
|
test('ExponentialBackoff: aborts with aborted string creates error', testExponentialBackoffRunAbortedStringCreatesError);
|
||||||
|
test('ExponentialBackoff: aborts with abort signal, skipping delay', testExponentialBackoffRunDelayAbortedWhenAbortSignal);
|
||||||
test('ExponentialBackoff: works via from and instance run', testExponentialBackoffFromAndInstanceRun);
|
test('ExponentialBackoff: works via from and instance run', testExponentialBackoffFromAndInstanceRun);
|
||||||
test('ExponentialBackoff: retries indefinitely when maxAttempts is 0', testExponentialBackoffRetriesIndefinitelyWhenMaxAttemptsIsZero);
|
test('ExponentialBackoff: retries indefinitely when maxAttempts is 0', testExponentialBackoffRetriesIndefinitelyWhenMaxAttemptsIsZero);
|
||||||
test('ExponentialBackoff: increases delay exponentially between attempts', testExponentialBackoffIncreasesDelayExponentially);
|
test('ExponentialBackoff: increases delay exponentially between attempts', testExponentialBackoffIncreasesDelayExponentially);
|
||||||
|
|||||||
Reference in New Issue
Block a user