diff --git a/src/BulkheadPolicy.test.ts b/src/BulkheadPolicy.test.ts index 2587c68..2a1acc4 100644 --- a/src/BulkheadPolicy.test.ts +++ b/src/BulkheadPolicy.test.ts @@ -120,7 +120,7 @@ describe('Bulkhead', () => { expect(b.queueSlots).to.equal(2); }); - it('links parent cancellation token', async () => { + it('links parent abort signal', async () => { const b = bulkhead(1, Infinity); const todo: Array> = []; for (let i = 0; i < 3; i++) { @@ -128,9 +128,9 @@ describe('Bulkhead', () => { todo.push( b.execute(async ({ signal }) => { await delay(1); - expect(signal.aborted).to.be.false; + expect(signal?.aborted).to.be.false; parent.abort(); - expect(signal.aborted).to.be.true; + expect(signal?.aborted).to.be.true; }, parent.signal), ); } diff --git a/src/BulkheadPolicy.ts b/src/BulkheadPolicy.ts index d6ef786..6c7ecb0 100644 --- a/src/BulkheadPolicy.ts +++ b/src/BulkheadPolicy.ts @@ -1,4 +1,3 @@ -import { neverAbortedSignal } from './common/abort'; import { defer } from './common/defer'; import { EventEmitter } from './common/Event'; import { ExecuteWrapper } from './common/Executor'; @@ -7,7 +6,7 @@ import { TaskCancelledError } from './errors/Errors'; import { IDefaultPolicyContext, IPolicy } from './Policy'; interface IQueueItem { - signal: AbortSignal; + signal?: AbortSignal; fn(context: IDefaultPolicyContext): Promise | T; resolve(value: T): void; reject(error: Error): void; @@ -62,9 +61,9 @@ export class BulkheadPolicy implements IPolicy { */ public async execute( fn: (context: IDefaultPolicyContext) => PromiseLike | T, - signal = neverAbortedSignal, + signal?: AbortSignal, ): Promise { - if (signal.aborted) { + if (signal?.aborted) { throw new TaskCancelledError(); } diff --git a/src/CircuitBreakerPolicy.test.ts b/src/CircuitBreakerPolicy.test.ts index ec908d8..8bd3121 100644 --- a/src/CircuitBreakerPolicy.test.ts +++ b/src/CircuitBreakerPolicy.test.ts @@ -163,15 +163,15 @@ describe('CircuitBreakerPolicy', () => { expect(await p.execute(() => 42)).to.equal(42); }); - it('links parent cancellation token', async () => { + it('links parent abort signal', async () => { const parent = new AbortController(); await circuitBreaker(handleAll, { halfOpenAfter: 1000, breaker: new ConsecutiveBreaker(3), }).execute(({ signal }) => { - expect(signal.aborted).to.be.false; + expect(signal?.aborted).to.be.false; parent.abort(); - expect(signal.aborted).to.be.true; + expect(signal?.aborted).to.be.true; }, parent.signal); }); diff --git a/src/CircuitBreakerPolicy.ts b/src/CircuitBreakerPolicy.ts index 83ff373..305eaf5 100644 --- a/src/CircuitBreakerPolicy.ts +++ b/src/CircuitBreakerPolicy.ts @@ -1,5 +1,4 @@ import { IBreaker } from './breaker/Breaker'; -import { neverAbortedSignal } from './common/abort'; import { EventEmitter } from './common/Event'; import { ExecuteWrapper, returnOrThrow } from './common/Executor'; import { BrokenCircuitError, TaskCancelledError } from './errors/Errors'; @@ -141,7 +140,7 @@ export class CircuitBreakerPolicy implements IPolicy { */ public async execute( fn: (context: IDefaultPolicyContext) => PromiseLike | T, - signal = neverAbortedSignal, + signal?: AbortSignal, ): Promise { const state = this.innerState; switch (state.value) { @@ -160,7 +159,7 @@ export class CircuitBreakerPolicy implements IPolicy { case CircuitState.HalfOpen: await state.test.catch(() => undefined); - if (this.state === CircuitState.Closed && signal.aborted) { + if (this.state === CircuitState.Closed && signal?.aborted) { throw new TaskCancelledError(); } @@ -185,7 +184,7 @@ export class CircuitBreakerPolicy implements IPolicy { private async halfOpen( fn: (context: IDefaultPolicyContext) => PromiseLike | T, - signal: AbortSignal, + signal?: AbortSignal, ): Promise { this.halfOpenEmitter.emit(); diff --git a/src/FallbackPolicy.test.ts b/src/FallbackPolicy.test.ts index 5b5f559..eb90682 100644 --- a/src/FallbackPolicy.test.ts +++ b/src/FallbackPolicy.test.ts @@ -25,12 +25,12 @@ describe('FallbackPolicy', () => { }); }); - it('links parent cancellation token', async () => { + it('links parent abort signal', async () => { const parent = new AbortController(); await fallback(handleAll, 'error').execute(({ signal }) => { - expect(signal.aborted).to.be.false; + expect(signal?.aborted).to.be.false; parent.abort(); - expect(signal.aborted).to.be.true; + expect(signal?.aborted).to.be.true; }, parent.signal); }); }); diff --git a/src/FallbackPolicy.ts b/src/FallbackPolicy.ts index 97b1a6a..2b05979 100644 --- a/src/FallbackPolicy.ts +++ b/src/FallbackPolicy.ts @@ -1,4 +1,3 @@ -import { neverAbortedSignal } from './common/abort'; import { ExecuteWrapper } from './common/Executor'; import { IDefaultPolicyContext, IPolicy } from './Policy'; @@ -24,7 +23,7 @@ export class FallbackPolicy implements IPolicy( fn: (context: IDefaultPolicyContext) => PromiseLike | T, - signal = neverAbortedSignal, + signal?: AbortSignal, ): Promise { const result = await this.executor.invoke(fn, { signal }); if ('success' in result) { diff --git a/src/NoopPolicy.ts b/src/NoopPolicy.ts index c339eeb..cee4915 100644 --- a/src/NoopPolicy.ts +++ b/src/NoopPolicy.ts @@ -1,4 +1,3 @@ -import { neverAbortedSignal } from './common/abort'; import { ExecuteWrapper, returnOrThrow } from './common/Executor'; import { IDefaultPolicyContext, IPolicy } from './Policy'; @@ -13,7 +12,7 @@ export class NoopPolicy implements IPolicy { public async execute( fn: (context: IDefaultPolicyContext) => PromiseLike | T, - signal: AbortSignal = neverAbortedSignal, + signal?: AbortSignal, ): Promise { return returnOrThrow(await this.executor.invoke(fn, { signal })); } diff --git a/src/Policy.test.ts b/src/Policy.test.ts index 3af095b..069a30e 100644 --- a/src/Policy.test.ts +++ b/src/Policy.test.ts @@ -142,14 +142,14 @@ describe('Policy', () => { }); }); - it('uses cancellation token in use', async () => { + it('uses abort signal in use', async () => { class Calculator { @usePolicy(retry(handleAll, { maxAttempts: 5 })) public double(n: number, context: IRetryContext) { expect(n).to.equal(2); - expect(context.signal.aborted).to.be.false; + expect(context.signal?.aborted).to.be.false; cts.abort(); - expect(context.signal.aborted).to.be.true; + expect(context.signal?.aborted).to.be.true; return n * 2; } } diff --git a/src/Policy.ts b/src/Policy.ts index 6404d59..3917522 100644 --- a/src/Policy.ts +++ b/src/Policy.ts @@ -63,7 +63,7 @@ export interface IDefaultPolicyContext { * Abort signal for the operation. This is propagated through multiple * retry policies. */ - signal: AbortSignal; + signal?: AbortSignal; } /** diff --git a/src/RetryPolicy.test.ts b/src/RetryPolicy.test.ts index be8fe91..728bbb8 100644 --- a/src/RetryPolicy.test.ts +++ b/src/RetryPolicy.test.ts @@ -159,9 +159,9 @@ describe('RetryPolicy', () => { await expect( retry(handleAll, { maxAttempts: 3 }).execute(({ signal }) => { calls++; - expect(signal.aborted).to.be.false; + expect(signal?.aborted).to.be.false; parent.abort(); - expect(signal.aborted).to.be.true; + expect(signal?.aborted).to.be.true; throw err; }, parent.signal), ).to.eventually.be.rejectedWith(err); diff --git a/src/RetryPolicy.ts b/src/RetryPolicy.ts index 23fbfc1..daa4b90 100644 --- a/src/RetryPolicy.ts +++ b/src/RetryPolicy.ts @@ -1,6 +1,5 @@ import { IBackoff, IBackoffFactory } from './backoff/Backoff'; import { ConstantBackoff } from './backoff/ConstantBackoff'; -import { neverAbortedSignal } from './common/abort'; import { EventEmitter } from './common/Event'; import { ExecuteWrapper } from './common/Executor'; import { FailureReason, IDefaultPolicyContext, IPolicy } from './Policy'; @@ -94,7 +93,7 @@ export class RetryPolicy implements IPolicy { */ public async execute( fn: (context: IRetryContext) => PromiseLike | T, - signal = neverAbortedSignal, + signal?: AbortSignal, ): Promise { const factory: IBackoffFactory> = this.options.backoff || new ConstantBackoff(0); @@ -105,7 +104,7 @@ export class RetryPolicy implements IPolicy { return result.success; } - if (!signal.aborted && retries < this.options.maxAttempts) { + if (!signal?.aborted && retries < this.options.maxAttempts) { const context = { attempt: retries + 1, signal, result }; backoff = backoff ? backoff.next(context) : factory.next(context); const delayDuration = backoff.duration; diff --git a/src/TimeoutPolicy.test.ts b/src/TimeoutPolicy.test.ts index 575c45e..fec8840 100644 --- a/src/TimeoutPolicy.test.ts +++ b/src/TimeoutPolicy.test.ts @@ -65,7 +65,7 @@ describe('TimeoutPolicy', () => { expect(output).to.be.empty; }); - it('links parent cancellation token', async () => { + it('links parent abort signal', async () => { const parent = new AbortController(); await timeout(1000, TimeoutStrategy.Cooperative).execute((_, signal) => { expect(signal.aborted).to.be.false; diff --git a/src/TimeoutPolicy.ts b/src/TimeoutPolicy.ts index 6c0d249..a19e063 100644 --- a/src/TimeoutPolicy.ts +++ b/src/TimeoutPolicy.ts @@ -6,7 +6,7 @@ import { IPolicy } from './Policy'; export enum TimeoutStrategy { /** - * Cooperative timeouts will simply revoke the inner cancellation token, + * Cooperative timeouts will simply abort the inner abort signal, * assuming the caller handles cancellation and throws or returns appropriately. */ Cooperative = 'optimistic', @@ -72,7 +72,7 @@ export class TimeoutPolicy implements IPolicy { /** * Executes the given function. - * @param fn Function to execute. Takes in a nested cancellation token. + * @param fn Function to execute. Takes in a nested abort signal. * @throws a {@link TaskCancelledError} if a timeout occurs */ public async execute( diff --git a/src/common/abort.ts b/src/common/abort.ts index be6d2d9..f8bd633 100644 --- a/src/common/abort.ts +++ b/src/common/abort.ts @@ -1,7 +1,5 @@ import { onAbort } from './Event'; -export const neverAbortedSignal = new AbortController().signal; - const cancelledSrc = new AbortController(); cancelledSrc.abort(); export const abortedSignal = cancelledSrc.signal; @@ -18,12 +16,11 @@ export const deriveAbortController = (signal?: AbortSignal) => { if (signal.aborted) { ctrl.abort(); + return ctrl; } - if (signal !== neverAbortedSignal) { - const ref = new WeakRef(ctrl); - onAbort(signal)(() => ref.deref()?.abort()); - } + const ref = new WeakRef(ctrl); + onAbort(signal)(() => ref.deref()?.abort()); return ctrl; };