Support `AbortController` (#26)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
This commit is contained in:
parent
0c28612eae
commit
1bf6679148
|
@ -41,6 +41,31 @@ export type Options = {
|
||||||
setTimeout: typeof global.setTimeout;
|
setTimeout: typeof global.setTimeout;
|
||||||
clearTimeout: typeof global.clearTimeout;
|
clearTimeout: typeof global.clearTimeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
You can abort the promise using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).
|
||||||
|
|
||||||
|
_Requires Node.js 16 or later._
|
||||||
|
|
||||||
|
@example
|
||||||
|
```
|
||||||
|
import pTimeout from 'p-timeout';
|
||||||
|
import delay from 'delay';
|
||||||
|
|
||||||
|
const delayedPromise = delay(3000);
|
||||||
|
|
||||||
|
const abortController = new AbortController();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
abortController.abort();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
await pTimeout(delayedPromise, 2000, undefined, {
|
||||||
|
signal: abortController.signal
|
||||||
|
});
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
signal?: globalThis.AbortSignal;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
42
index.js
42
index.js
|
@ -5,8 +5,39 @@ export class TimeoutError extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
An error to be thrown when the request is aborted by AbortController.
|
||||||
|
DOMException is thrown instead of this Error when DOMException is available.
|
||||||
|
*/
|
||||||
|
export class AbortError extends Error {
|
||||||
|
constructor(message) {
|
||||||
|
super();
|
||||||
|
this.name = 'AbortError';
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
TODO: Remove AbortError and just throw DOMException when targeting Node 18.
|
||||||
|
*/
|
||||||
|
const getDOMException = errorMessage => globalThis.DOMException === undefined ?
|
||||||
|
new AbortError(errorMessage) :
|
||||||
|
new DOMException(errorMessage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
TODO: Remove below function and just 'reject(signal.reason)' when targeting Node 18.
|
||||||
|
*/
|
||||||
|
const getAbortedReason = signal => {
|
||||||
|
const reason = signal.reason === undefined ?
|
||||||
|
getDOMException('This operation was aborted.') :
|
||||||
|
signal.reason;
|
||||||
|
|
||||||
|
return reason instanceof Error ? reason : getDOMException(reason);
|
||||||
|
};
|
||||||
|
|
||||||
export default function pTimeout(promise, milliseconds, fallback, options) {
|
export default function pTimeout(promise, milliseconds, fallback, options) {
|
||||||
let timer;
|
let timer;
|
||||||
|
|
||||||
const cancelablePromise = new Promise((resolve, reject) => {
|
const cancelablePromise = new Promise((resolve, reject) => {
|
||||||
if (typeof milliseconds !== 'number' || Math.sign(milliseconds) !== 1) {
|
if (typeof milliseconds !== 'number' || Math.sign(milliseconds) !== 1) {
|
||||||
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
||||||
|
@ -22,6 +53,17 @@ export default function pTimeout(promise, milliseconds, fallback, options) {
|
||||||
...options
|
...options
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (options.signal) {
|
||||||
|
const {signal} = options;
|
||||||
|
if (signal.aborted) {
|
||||||
|
reject(getAbortedReason(signal));
|
||||||
|
}
|
||||||
|
|
||||||
|
signal.addEventListener('abort', () => {
|
||||||
|
reject(getAbortedReason(signal));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
timer = options.customTimers.setTimeout.call(undefined, () => {
|
timer = options.customTimers.setTimeout.call(undefined, () => {
|
||||||
if (typeof fallback === 'function') {
|
if (typeof fallback === 'function') {
|
||||||
try {
|
try {
|
||||||
|
|
25
readme.md
25
readme.md
|
@ -103,6 +103,31 @@ await pTimeout(doSomething(), 2000, undefined, {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### signal
|
||||||
|
|
||||||
|
Type: [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
|
||||||
|
|
||||||
|
You can abort the promise using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).
|
||||||
|
|
||||||
|
*Requires Node.js 16 or later.*
|
||||||
|
|
||||||
|
```js
|
||||||
|
import pTimeout from 'p-timeout';
|
||||||
|
import delay from 'delay';
|
||||||
|
|
||||||
|
const delayedPromise = delay(3000);
|
||||||
|
|
||||||
|
const abortController = new AbortController();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
abortController.abort();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
await pTimeout(delayedPromise, 2000, undefined, {
|
||||||
|
signal: abortController.signal
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### TimeoutError
|
### TimeoutError
|
||||||
|
|
||||||
Exposed for instance checking and sub-classing.
|
Exposed for instance checking and sub-classing.
|
||||||
|
|
31
test.js
31
test.js
|
@ -88,3 +88,34 @@ test('`.clear()` method', async t => {
|
||||||
await promise;
|
await promise;
|
||||||
t.true(inRange(end(), {start: 0, end: 350}));
|
t.true(inRange(end(), {start: 0, end: 350}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
TODO: Remove if statement when targeting Node.js 16.
|
||||||
|
*/
|
||||||
|
if (globalThis.AbortController !== undefined) {
|
||||||
|
test('rejects when calling `AbortController#abort()`', async t => {
|
||||||
|
const abortController = new AbortController();
|
||||||
|
|
||||||
|
const promise = pTimeout(delay(3000), 2000, undefined, {
|
||||||
|
signal: abortController.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
abortController.abort();
|
||||||
|
|
||||||
|
await t.throwsAsync(promise, {
|
||||||
|
name: 'AbortError'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('already aborted signal', async t => {
|
||||||
|
const abortController = new AbortController();
|
||||||
|
|
||||||
|
abortController.abort();
|
||||||
|
|
||||||
|
await t.throwsAsync(pTimeout(delay(3000), 2000, undefined, {
|
||||||
|
signal: abortController.signal
|
||||||
|
}), {
|
||||||
|
name: 'AbortError'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue