Skip to content

Commit

Permalink
Allow passing Infinity to waitFor as timeout (#3215)
Browse files Browse the repository at this point in the history
* Allow passing `Infinity` to as `waitFor` timeout

* Add changeset

* Add dev warning for negative timeouts

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>

* Format

* Add test

* Simplify casting situation in `waitFor`

* Update .changeset/cool-ducks-pretend.md

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
  • Loading branch information
tom-sherman and Andarist committed Apr 15, 2022
1 parent 7a698d4 commit 44c66e7
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 5 deletions.
20 changes: 20 additions & 0 deletions .changeset/cool-ducks-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
'xstate': patch
---

Removing the timeout that's built in to `waitFor` is now supported by explicitly passing an `Infinity` value.

Example usage:

```js
import { waitFor } from 'xstate/lib/waitFor';

// This will
const loggedInState = await waitFor(
loginService,
(state) => state.hasTag('loggedIn'),
{ timeout: Infinity }
);
```

This fixes a bug that causes `waitFor` to reject with an error immediately due to the behaviour of `setTimeout`.
18 changes: 13 additions & 5 deletions packages/core/src/waitFor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,21 @@ export function waitFor<TActorRef extends ActorRef<any, any>>(
};
return new Promise((res, rej) => {
let done = false;
const handle = setTimeout(() => {
sub.unsubscribe();
rej(new Error(`Timeout of ${resolvedOptions.timeout} ms exceeded`));
}, resolvedOptions.timeout);
if (process.env.NODE_ENV !== 'production' && resolvedOptions.timeout < 0) {
console.error(
'`timeout` passed to `waitFor` is negative and it will reject its internal promise immediately.'
);
}
const handle =
resolvedOptions.timeout === Infinity
? undefined
: setTimeout(() => {
sub.unsubscribe();
rej(new Error(`Timeout of ${resolvedOptions.timeout} ms exceeded`));
}, resolvedOptions.timeout);

const dispose = () => {
clearTimeout(handle);
clearTimeout(handle!);
done = true;
sub?.unsubscribe();
};
Expand Down
25 changes: 25 additions & 0 deletions packages/core/test/waitFor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,31 @@ describe('waitFor', () => {
}
});

it('should not reject immediately when passing Infinity as timeout', async () => {
const machine = createMachine({
initial: 'a',
states: {
a: {
on: { NEXT: 'b' }
},
b: {
on: { NEXT: 'c' }
},
c: {}
}
});
const service = interpret(machine).start();
const result = await Promise.race([
waitFor(service, (state) => state.matches('c'), {
timeout: Infinity
}),
new Promise((res) => setTimeout(res, 10)).then(() => 'timeout')
]);

expect(result).toBe('timeout');
service.stop();
});

it('should throw an error when reaching a final state that does not match the predicate', async () => {
const machine = createMachine({
initial: 'a',
Expand Down

0 comments on commit 44c66e7

Please sign in to comment.