Make timeout clearable (#15)

Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
This commit is contained in:
peja 2020-12-26 13:42:06 +03:00 committed by GitHub
parent 46c25acfc5
commit e26f08d43f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 76 additions and 45 deletions

15
index.d.ts vendored
View File

@ -40,6 +40,13 @@ declare namespace pTimeout {
}; };
} }
interface ClearablePromise<T> extends Promise<T>{
/**
Clear the timeout.
*/
clear: () => void;
}
declare const pTimeout: { declare const pTimeout: {
TimeoutError: typeof TimeoutErrorClass; TimeoutError: typeof TimeoutErrorClass;
@ -53,7 +60,7 @@ declare const pTimeout: {
@param input - Promise to decorate. @param input - Promise to decorate.
@param milliseconds - Milliseconds before timing out. @param milliseconds - Milliseconds before timing out.
@param message - Specify a custom error message or error. If you do a custom error, it's recommended to sub-class `pTimeout.TimeoutError`. Default: `'Promise timed out after 50 milliseconds'`. @param message - Specify a custom error message or error. If you do a custom error, it's recommended to sub-class `pTimeout.TimeoutError`. Default: `'Promise timed out after 50 milliseconds'`.
@returns A decorated `input` that times out after `milliseconds` time. @returns A decorated `input` that times out after `milliseconds` time. It has a `.clear()` method that clears the timeout.
@example @example
``` ```
@ -71,7 +78,7 @@ declare const pTimeout: {
milliseconds: number, milliseconds: number,
message?: string | Error, message?: string | Error,
options?: pTimeout.Options options?: pTimeout.Options
): Promise<ValueType>; ): ClearablePromise<ValueType>;
/** /**
Timeout a promise after a specified amount of time. Timeout a promise after a specified amount of time.
@ -81,7 +88,7 @@ declare const pTimeout: {
@param input - Promise to decorate. @param input - Promise to decorate.
@param milliseconds - Milliseconds before timing out. Passing `Infinity` will cause it to never time out. @param milliseconds - Milliseconds before timing out. Passing `Infinity` will cause it to never time out.
@param fallback - Do something other than rejecting with an error on timeout. You could for example retry. @param fallback - Do something other than rejecting with an error on timeout. You could for example retry.
@returns A decorated `input` that times out after `milliseconds` time. @returns A decorated `input` that times out after `milliseconds` time. It has a `.clear()` method that clears the timeout.
@example @example
``` ```
@ -100,7 +107,7 @@ declare const pTimeout: {
milliseconds: number, milliseconds: number,
fallback: () => ReturnType | Promise<ReturnType>, fallback: () => ReturnType | Promise<ReturnType>,
options?: pTimeout.Options options?: pTimeout.Options
): Promise<ValueType | ReturnType>; ): ClearablePromise<ValueType | ReturnType>;
}; };
export = pTimeout; export = pTimeout;

View File

@ -7,7 +7,9 @@ class TimeoutError extends Error {
} }
} }
const pTimeout = (promise, milliseconds, fallback, options) => new Promise((resolve, reject) => { const pTimeout = (promise, milliseconds, fallback, options) => {
let timer;
const cancelablePromise = new Promise((resolve, reject) => {
if (typeof milliseconds !== 'number' || milliseconds < 0) { if (typeof milliseconds !== 'number' || milliseconds < 0) {
throw new TypeError('Expected `milliseconds` to be a positive number'); throw new TypeError('Expected `milliseconds` to be a positive number');
} }
@ -22,7 +24,7 @@ const pTimeout = (promise, milliseconds, fallback, options) => new Promise((reso
...options ...options
}; };
const timer = options.customTimers.setTimeout.call(undefined, () => { timer = options.customTimers.setTimeout.call(undefined, () => {
if (typeof fallback === 'function') { if (typeof fallback === 'function') {
try { try {
resolve(fallback()); resolve(fallback());
@ -52,7 +54,15 @@ const pTimeout = (promise, milliseconds, fallback, options) => new Promise((reso
options.customTimers.clearTimeout.call(undefined, timer); options.customTimers.clearTimeout.call(undefined, timer);
} }
})(); })();
}); });
cancelablePromise.clear = () => {
clearTimeout(timer);
timer = undefined;
};
return cancelablePromise;
};
module.exports = pTimeout; module.exports = pTimeout;
// TODO: Remove this for the next major release // TODO: Remove this for the next major release

View File

@ -37,6 +37,8 @@
"delay": "^4.4.0", "delay": "^4.4.0",
"p-cancelable": "^2.0.0", "p-cancelable": "^2.0.0",
"tsd": "^0.13.1", "tsd": "^0.13.1",
"xo": "^0.35.0" "xo": "^0.35.0",
"in-range": "^2.0.0",
"time-span": "^4.0.0"
} }
} }

View File

@ -25,7 +25,7 @@ pTimeout(delayedPromise, 50).then(() => 'foo');
### pTimeout(input, milliseconds, message?, options?) ### pTimeout(input, milliseconds, message?, options?)
### pTimeout(input, milliseconds, fallback?, options?) ### pTimeout(input, milliseconds, fallback?, options?)
Returns a decorated `input` that times out after `milliseconds` time. Returns a decorated `input` that times out after `milliseconds` time. It has a `.clear()` method that clears the timeout.
If you pass in a cancelable promise, specifically a promise with a `.cancel()` method, that method will be called when the `pTimeout` promise times out. If you pass in a cancelable promise, specifically a promise with a `.cancel()` method, that method will be called when the `pTimeout` promise times out.

12
test.js
View File

@ -1,6 +1,8 @@
import test from 'ava'; import test from 'ava';
import delay from 'delay'; import delay from 'delay';
import PCancelable from 'p-cancelable'; import PCancelable from 'p-cancelable';
import inRange from 'in-range';
import timeSpan from 'time-span';
import pTimeout from '.'; import pTimeout from '.';
const fixture = Symbol('fixture'); const fixture = Symbol('fixture');
@ -72,3 +74,13 @@ test('accepts `customTimers` option', async t => {
} }
}); });
}); });
test('`.clear()` method', async t => {
const end = timeSpan();
const promise = pTimeout(delay(300), 200);
promise.clear();
await promise;
t.true(inRange(end(), {start: 0, end: 350}));
});