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

Add delay.createWithTimers() #42

Merged
merged 4 commits into from
Oct 10, 2018
Merged
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
6 changes: 5 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface Options {
signal?: AbortSignal
}

declare const delay: {
type Delay = {
/**
* Create a promise which resolves after the specified `milliseconds`.
*
Expand Down Expand Up @@ -46,6 +46,10 @@ declare const delay: {
/** Value to reject in the returned promise. */
value?: any
}): ClearablePromise<never>;
}

declare const delay: Delay & {
createWithTimers(timers: {clearTimeout: typeof clearTimeout, setTimeout: typeof setTimeout}): Delay
};

export default delay;
17 changes: 11 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const createAbortError = () => {
return error;
};

const createDelay = willResolve => (ms, {value, signal} = {}) => {
const createDelay = ({clearTimeout: clear = clearTimeout, setTimeout: set = setTimeout, willResolve}) => (ms, {value, signal} = {}) => {
if (signal && signal.aborted) {
return Promise.reject(createAbortError());
}
Expand All @@ -16,7 +16,7 @@ const createDelay = willResolve => (ms, {value, signal} = {}) => {
let rejectFn;

const signalListener = () => {
clearTimeout(timeoutId);
clear(timeoutId);
rejectFn(createAbortError());
};

Expand All @@ -36,15 +36,15 @@ const createDelay = willResolve => (ms, {value, signal} = {}) => {
}
};
rejectFn = reject;
timeoutId = setTimeout(settle, ms);
timeoutId = set(settle, ms);
});

if (signal) {
signal.addEventListener('abort', signalListener, {once: true});
}

delayPromise.clear = () => {
clearTimeout(timeoutId);
clear(timeoutId);
timeoutId = null;
cleanup();
settle();
Expand All @@ -53,7 +53,12 @@ const createDelay = willResolve => (ms, {value, signal} = {}) => {
return delayPromise;
};

const delay = createDelay(true);
delay.reject = createDelay(false);
const delay = createDelay({willResolve: true});
delay.reject = createDelay({willResolve: false});
delay.createWithTimers = ({clearTimeout, setTimeout}) => {
const delay = createDelay({clearTimeout, setTimeout, willResolve: true});
delay.reject = createDelay({clearTimeout, setTimeout, willResolve: false});
return delay;
};
module.exports = delay;
module.exports.default = delay;
9 changes: 9 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,12 @@ expectType<number>(await delay(200, {value: 0}));

expectType<never>(await delay.reject(200, {value: '🦄'}));
expectType<never>(await delay.reject(200, {value: 0}));

const customDelay = delay.createWithTimers({clearTimeout, setTimeout})
expectType<void>(await customDelay(200));

expectType<string>(await customDelay(200, {value: '🦄'}));
expectType<number>(await customDelay(200, {value: 0}));

expectType<never>(await customDelay.reject(200, {value: '🦄'}));
expectType<never>(await customDelay.reject(200, {value: 0}));
19 changes: 19 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ The returned promise will be rejected with an AbortError if the signal is aborte

Clears the delay and settles the promise.

### delay.createWithTimers({clearTimeout, setTimeout})

Creates a new `delay` instance using the provided functions for clearing and setting timeouts. Useful if you're about to stub timers globally, but you still want to use `delay` to manage your tests.

## Advanced usage

Expand Down Expand Up @@ -137,6 +140,22 @@ const delay = require('delay');
})();
```

Create a new instance that is unaffected by libraries such as [lolex](https://github.com/sinonjs/lolex/):

```js
const delay = require('delay');

const customDelay = delay.createWithTimers({clearTimeout, setTimeout});

(async() => {
const result = await customDelay(100, {value: '🦄'});

// Executed after 100 milliseconds
console.log(result);
//=> '🦄'
})();
```


## Related

Expand Down
39 changes: 39 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,42 @@ test('rejects with AbortError if AbortSignal is already aborted', async t => {
);
t.true(end() < 30);
});

test('can create a new instance with fixed timeout methods', async t => {
const cleared = [];
const callbacks = [];
const custom = m.createWithTimers({
clearTimeout(handle) {
cleared.push(handle);
},

setTimeout(callback, ms) {
const handle = Symbol('handle');
callbacks.push({callback, handle, ms});
return handle;
}
});

const first = custom(50, {value: 'first'});
t.is(callbacks.length, 1);
t.is(callbacks[0].ms, 50);
callbacks[0].callback();
t.is(await first, 'first');

const second = custom.reject(40, {value: 'second'});
t.is(callbacks.length, 2);
t.is(callbacks[1].ms, 40);
callbacks[1].callback();
try {
await second;
} catch (error) {
t.is(error, 'second');
}

const third = custom(60);
t.is(callbacks.length, 3);
t.is(callbacks[2].ms, 60);
third.clear();
t.is(cleared.length, 1);
t.is(cleared[0], callbacks[2].handle);
});