Skip to content

Commit

Permalink
Allow timing out without erroring (#30)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
fregante and sindresorhus committed Jan 23, 2023
1 parent 200df03 commit 45405e7
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 10 deletions.
22 changes: 19 additions & 3 deletions index.d.ts
Expand Up @@ -43,11 +43,23 @@ export type Options<ReturnType> = {
fallback?: () => ReturnType | Promise<ReturnType>;

/**
Specify a custom error message or error.
Specify a custom error message or error to throw when it times out:
If you do a custom error, it's recommended to sub-class `pTimeout.TimeoutError`.
- `message: 'too slow'` will throw `TimeoutError('too slow')`
- `message: new MyCustomError('it’s over 9000')` will throw the same error instance
- `message: false` will make the promise resolve with `undefined` instead of rejecting
If you do a custom error, it's recommended to sub-class `TimeoutError`:
```
import {TimeoutError} from 'p-timeout';
class MyCustomError extends TimeoutError {
name = "MyCustomError";
}
```
*/
message?: string | Error;
message?: string | Error | false;

/**
Custom implementations for the `setTimeout` and `clearTimeout` functions.
Expand Down Expand Up @@ -129,6 +141,10 @@ await pTimeout(delayedPromise(), {
});
```
*/
export default function pTimeout<ValueType, ReturnType = ValueType>(
input: PromiseLike<ValueType>,
options: Options<ReturnType> & {message: false}
): ClearablePromise<ValueType | ReturnType | undefined>;
export default function pTimeout<ValueType, ReturnType = ValueType>(
input: PromiseLike<ValueType>,
options: Options<ReturnType>
Expand Down
12 changes: 8 additions & 4 deletions index.js
Expand Up @@ -77,14 +77,18 @@ export default function pTimeout(promise, options) {
return;
}

const errorMessage = typeof message === 'string' ? message : `Promise timed out after ${milliseconds} milliseconds`;
const timeoutError = message instanceof Error ? message : new TimeoutError(errorMessage);

if (typeof promise.cancel === 'function') {
promise.cancel();
}

reject(timeoutError);
if (message === false) {
resolve();
} else if (message instanceof Error) {
reject(message);
} else {
const errorMessage = message ?? `Promise timed out after ${milliseconds} milliseconds`;
reject(new TimeoutError(errorMessage));
}
}, milliseconds);

(async () => {
Expand Down
3 changes: 3 additions & 0 deletions index.test-d.ts
Expand Up @@ -16,6 +16,9 @@ pTimeout(delayedPromise(), {milliseconds: 50}).then(value => {
pTimeout(delayedPromise(), {milliseconds: 50, message: 'error'}).then(value => {
expectType<string>(value);
});
pTimeout(delayedPromise(), {milliseconds: 50, message: false}).then(value => {
expectType<string | undefined>(value);
});
pTimeout(delayedPromise(), {milliseconds: 50, message: new Error('error')}).then(value => {
expectType<string>(value);
});
Expand Down
18 changes: 15 additions & 3 deletions readme.md
Expand Up @@ -50,12 +50,24 @@ Passing `Infinity` will cause it to never time out.

##### message

Type: `string | Error`\
Type: `string | Error | false`\
Default: `'Promise timed out after 50 milliseconds'`

Specify a custom error message or error.
Specify a custom error message or error to throw when it times out:

If you do a custom error, it's recommended to sub-class `pTimeout.TimeoutError`.
- `message: 'too slow'` will throw `TimeoutError('too slow')`
- `message: new MyCustomError('it’s over 9000')` will throw the same error instance
- `message: false` will make the promise resolve with `undefined` instead of rejecting

If you do a custom error, it's recommended to sub-class `TimeoutError`:

```js
import {TimeoutError} from 'p-timeout';

class MyCustomError extends TimeoutError {
name = "MyCustomError";
}
```

##### fallback

Expand Down
7 changes: 7 additions & 0 deletions test.js
Expand Up @@ -35,6 +35,13 @@ test('rejects after timeout', async t => {
await t.throwsAsync(pTimeout(delay(200), {milliseconds: 50}), {instanceOf: TimeoutError});
});

test('resolves after timeout with message:false', async t => {
t.is(
await pTimeout(delay(200), {milliseconds: 50, message: false}),
undefined,
);
});

test('rejects before timeout if specified promise rejects', async t => {
await t.throwsAsync(pTimeout(delay(50).then(() => {
throw fixtureError;
Expand Down

0 comments on commit 45405e7

Please sign in to comment.