Skip to content

Commit

Permalink
Expose all timeout options (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
jopemachine committed Jul 29, 2022
1 parent c25a617 commit 1f5d760
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 16 deletions.
34 changes: 31 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export interface Options {
import {Options as TimeoutOptions} from 'p-timeout';

export interface Options<ResolveValueType> {
/**
Number of milliseconds to wait after `condition` resolves to `false` before calling it again.
Expand All @@ -9,9 +11,35 @@ export interface Options {
/**
Number of milliseconds to wait before automatically rejecting with a `TimeoutError`.
You can customize the timeout `Error` by specifying `TimeoutOptions`.
@default Infinity
@example
```
import pWaitFor from 'p-wait-for';
import {pathExists} from 'path-exists';
const originalSetTimeout = setTimeout;
const originalClearTimeout = clearTimeout;
sinon.useFakeTimers();
await pWaitFor(() => pathExists('unicorn.png'), {
timeout: {
milliseconds: 100,
message: new MyError('Time’s up!'),
customTimers: {
setTimeout: originalSetTimeout,
clearTimeout: originalClearTimeout
}
}
});
console.log('Yay! The file now exists.');
```
*/
readonly timeout?: number;
readonly timeout?: number | TimeoutOptions<ResolveValueType>;

/**
Whether to run the check immediately rather than starting by waiting `interval` milliseconds.
Expand Down Expand Up @@ -45,7 +73,7 @@ declare const pWaitFor: {
console.log('Yay! The file now exists.');
```
*/
<ResolveValueType>(condition: () => PromiseLike<boolean> | boolean | ResolveValue<ResolveValueType> | PromiseLike<ResolveValue<ResolveValueType>>, options?: Options): Promise<ResolveValueType>;
<ResolveValueType>(condition: () => PromiseLike<boolean> | boolean | ResolveValue<ResolveValueType> | PromiseLike<ResolveValue<ResolveValueType>>, options?: Options<ResolveValueType>): Promise<ResolveValueType>;

/**
Resolve the main promise with a custom value.
Expand Down
18 changes: 7 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,15 @@ export default async function pWaitFor(condition, options = {}) {
}
});

if (timeout !== Number.POSITIVE_INFINITY) {
try {
return await pTimeout(promise, timeout);
} catch (error) {
if (retryTimeout) {
clearTimeout(retryTimeout);
}

throw error;
}
if (timeout === Number.POSITIVE_INFINITY) {
return promise;
}

return promise;
try {
return await pTimeout(promise, typeof timeout === 'number' ? {milliseconds: timeout} : timeout);
} finally {
clearTimeout(retryTimeout);
}
}

pWaitFor.resolveWith = value => ({[resolveValue]: value});
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"bluebird"
],
"dependencies": {
"p-timeout": "^5.0.0"
"p-timeout": "^6.0.0"
},
"devDependencies": {
"ava": "^3.15.0",
Expand Down
75 changes: 74 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,84 @@ Number of milliseconds to wait after `condition` resolves to `false` before call

##### timeout

Type: `number`\
Type: `number | TimeoutOptions`\
Default: `Infinity`

Number of milliseconds to wait before automatically rejecting with a `TimeoutError`.

You can customize the timeout `Error` by specifying `TimeoutOptions`.

```js
import pWaitFor from 'p-wait-for';
import {pathExists} from 'path-exists';

const originalSetTimeout = setTimeout;
const originalClearTimeout = clearTimeout;

sinon.useFakeTimers();

await pWaitFor(() => pathExists('unicorn.png'), {
timeout: {
milliseconds: 100,
message: new MyError('Time’s up!'),
customTimers: {
setTimeout: originalSetTimeout,
clearTimeout: originalClearTimeout
}
}
});

console.log('Yay! The file now exists.');
```

###### milliseconds

Type: `number`\
Default: `Infinity`

Milliseconds before timing out.

Passing `Infinity` will cause it to never time out.

###### message

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

Specify a custom error message or error.

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

###### customTimers

Type: `object` with function properties `setTimeout` and `clearTimeout`

Custom implementations for the `setTimeout` and `clearTimeout` functions.

Useful for testing purposes, in particular to work around [`sinon.useFakeTimers()`](https://sinonjs.org/releases/latest/fake-timers/).

###### fallback

Type: `Function`

Do something other than rejecting with an error on timeout.

Example:

```js
import pWaitFor from 'p-wait-for';
import {pathExists} from 'path-exists';

await pWaitFor(() => pathExists('unicorn.png'), {
timeout: {
milliseconds: 50,
fallback: () => {
console.log('Time’s up! executed the fallback function!');
},
}
});
```

##### before

Type: `boolean`\
Expand Down
24 changes: 24 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,27 @@ test('does not perform a leading check', async t => {
test('resolveWith()', async t => {
t.true(await pWaitFor(() => pWaitFor.resolveWith(true)));
});

test('timeout option - object', async t => {
class CustomizedTimeoutError extends Error {
constructor() {
super();
this.name = 'MyError';
this.message = 'Time’s up!';
}
}

await t.throwsAsync(pWaitFor(async () => {
await delay(1000);
return true;
}, {
timeout: {
milliseconds: 100,
message: new CustomizedTimeoutError()
}
}), {
name: 'MyError',
message: 'Time’s up!',
instanceOf: CustomizedTimeoutError
});
});

0 comments on commit 1f5d760

Please sign in to comment.