Skip to content

Commit

Permalink
feat: add new option abortOnReturn to timeouts
Browse files Browse the repository at this point in the history
  • Loading branch information
connor4312 committed Mar 10, 2023
1 parent cdf45ad commit 98da5a4
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
}
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 3.1.0

- **feat:** add new option `abortOnReturn` to timeouts ([#72](https://github.com/connor4312/cockatiel/issues/72))

## 3.0.0

- **breaking:** please see the breaking changes for the two 3.0.0-beta releases
Expand Down
10 changes: 8 additions & 2 deletions src/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,14 @@ export function usePolicy(policy: IPolicy<IDefaultPolicyContext, never>) {
* {@link TaskCancelledError} when the timeout is reached, in addition to
* marking the passed token as failed.
*/
export function timeout(duration: number, strategy: TimeoutStrategy) {
return new TimeoutPolicy(duration, strategy);
export function timeout(
duration: number,
strategyOrOpts: TimeoutStrategy | { strategy: TimeoutStrategy; abortOnReturn: boolean },
) {
return new TimeoutPolicy(
duration,
typeof strategyOrOpts === 'string' ? { strategy: strategyOrOpts } : strategyOrOpts,
);
}

/**
Expand Down
18 changes: 18 additions & 0 deletions src/TimeoutPolicy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,24 @@ describe('TimeoutPolicy', () => {
}, parent.signal);
});

it('aborts on return by default', async () => {
let signal: AbortSignal;
await timeout(1, TimeoutStrategy.Cooperative).execute(async (_, s) => {
signal = s;
});
expect(signal!.aborted).to.be.true;
});

it('does not aborts on return if requested', async () => {
let signal: AbortSignal;
await timeout(1, { strategy: TimeoutStrategy.Aggressive, abortOnReturn: false }).execute(
async (_, s) => {
signal = s;
},
);
expect(signal!.aborted).to.be.false;
});

describe('events', () => {
let onSuccess: SinonStub;
let onFailure: SinonStub;
Expand Down
20 changes: 16 additions & 4 deletions src/TimeoutPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ export interface ICancellationContext {
signal: AbortSignal;
}

export interface ITimeoutOptions {
/** Strategy for timeouts, "Cooperative", or "Accessive" */
strategy: TimeoutStrategy;
/**
* Whether the AbortSignal should be aborted when the
* function returns. Defaults to true.
*/
abortOnReturn?: boolean;
}

export class TimeoutPolicy implements IPolicy<ICancellationContext> {
declare readonly _altReturn: never;

Expand All @@ -43,7 +53,7 @@ export class TimeoutPolicy implements IPolicy<ICancellationContext> {

constructor(
private readonly duration: number,
private readonly strategy: TimeoutStrategy,
private readonly options: ITimeoutOptions,
private readonly executor = new ExecuteWrapper(),
private readonly unref = false,
) {}
Expand All @@ -56,7 +66,7 @@ export class TimeoutPolicy implements IPolicy<ICancellationContext> {
* timeout might still be happening.
*/
public dangerouslyUnref() {
const t = new TimeoutPolicy(this.duration, this.strategy, this.executor, true);
const t = new TimeoutPolicy(this.duration, this.options, this.executor, true);
return t;
}

Expand All @@ -81,7 +91,7 @@ export class TimeoutPolicy implements IPolicy<ICancellationContext> {
const onCancelledListener = onceAborted(() => this.timeoutEmitter.emit());

try {
if (this.strategy === TimeoutStrategy.Cooperative) {
if (this.options.strategy === TimeoutStrategy.Cooperative) {
return returnOrThrow(await this.executor.invoke(fn, context, aborter.signal));
}

Expand All @@ -97,7 +107,9 @@ export class TimeoutPolicy implements IPolicy<ICancellationContext> {
.then(returnOrThrow);
} finally {
onCancelledListener.dispose();
aborter.abort();
if (this.options.abortOnReturn !== false) {
aborter.abort();
}
clearTimeout(timer);
}
}
Expand Down

0 comments on commit 98da5a4

Please sign in to comment.