Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support AbortSignal to listen to execution abortion #48

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,37 @@ export type Options = {
```
*/
readonly onDelay?: () => void;

/**
`AbortSignal` to abort pending executions. All unresolved promises are rejected with a `pThrottle.AbortError` error.

@example
```
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs import statement

const abortController = new AbortController();

const throttled = pThrottle({
limit: 3,
interval: 1000,
signal: abortController.signal
})(() => {
console.log('Executing...');
});

const throttled = throttle(() => {
console.log('Executing...');
});

await throttled();
await throttled();
abortController.abort();
let promise = throttled();
await promise;
//=> Executing...
//=> Executing...
//=> Promise rejected with AbortError (DOMException)
```
*/
readonly signal?: AbortSignal;
};

/**
Expand Down
6 changes: 5 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export class AbortError extends Error {
}
}

export default function pThrottle({limit, interval, strict, onDelay}) {
export default function pThrottle({limit, interval, strict, onDelay, signal}) {
if (!Number.isFinite(limit)) {
throw new TypeError('Expected `limit` to be a finite number');
}
Expand Down Expand Up @@ -109,6 +109,10 @@ export default function pThrottle({limit, interval, strict, onDelay}) {
},
});

if (signal) {
signal.addEventListener('abort', throttled.abort);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also need to use AbortSignal.throwIfAborted() as early as possible in the function in case the signal was already aborted when this is called.

Comment on lines +112 to +114
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (signal) {
signal.addEventListener('abort', throttled.abort);
}
signal?.addEventListener('abort', throttled.abort);


return throttled;
};
}
32 changes: 32 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,38 @@ await throttled();
//=> Executing...
```

##### signal

Type: [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)

An optional signal to listen for abort events. If the signal becomes aborted, all pending and future calls are rejected with a `pThrottle.AbortError` error.
Copy link
Owner

@sindresorhus sindresorhus Feb 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the correct behavior. It should reject with AbortSignal.reason


You can abort the promises using an [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController):

```js
import pThrottle from 'p-throttle';

const abortController = new AbortController();
const throttle = pThrottle({
limit: 2,
interval: 1000,
signal: abortController.signal
});

const throttled = throttle(() => {
console.log('Executing...');
});

await throttled();
await throttled();
abortController.abort();
let promise = throttled();
await promise;
//=> Executing...
//=> Executing...
//=> Promise rejected with AbortError (DOMException)
```

### throttle(function_)

Returns a throttled version of `function_`.
Expand Down
24 changes: 24 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,30 @@ test('can be aborted', async t => {
t.true(end() < 100);
});

test('can listen to AbortSignal to abort execution', async t => {
const limit = 1;
const interval = 10_000; // 10 seconds
const end = timeSpan();
const abortController = new AbortController();
const throttled = pThrottle({limit, interval, signal: abortController.signal})(async x => x);

const one = await throttled(1);
const promise = throttled(2);
abortController.abort();
let error;
let endValue;
try {
endValue = await promise;
} catch (error_) {
error = error_;
}
Comment on lines +166 to +170
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use t.throwsAsync


t.true(error instanceof AbortError);
t.true(end() < 100);
t.true(one === 1);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use t.is

t.true(endValue === undefined);
});

test('can be disabled', async t => {
let counter = 0;

Expand Down
Loading