File tree 2 files changed +30
-5
lines changed
2 files changed +30
-5
lines changed Original file line number Diff line number Diff line change @@ -9,14 +9,14 @@ afterAll(() => {
9
9
jest . useRealTimers ( )
10
10
} )
11
11
12
- async function runWaitFor ( ) {
12
+ async function runWaitFor ( { time = 300 } = { } , options ) {
13
13
const response = 'data'
14
14
const doAsyncThing = ( ) =>
15
- new Promise ( r => setTimeout ( ( ) => r ( response ) , 300 ) )
15
+ new Promise ( r => setTimeout ( ( ) => r ( response ) , time ) )
16
16
let result
17
17
doAsyncThing ( ) . then ( r => ( result = r ) )
18
18
19
- await waitFor ( ( ) => expect ( result ) . toBe ( response ) )
19
+ await waitFor ( ( ) => expect ( result ) . toBe ( response ) , options )
20
20
}
21
21
22
22
test ( 'real timers' , async ( ) => {
@@ -64,3 +64,17 @@ test('times out after 1000ms by default', async () => {
64
64
// that we're using real timers.
65
65
expect ( performance . now ( ) - start ) . toBeGreaterThanOrEqual ( 900 )
66
66
} )
67
+
68
+ test ( 'recursive timers do not cause issues' , async ( ) => {
69
+ let recurse = true
70
+ function startTimer ( ) {
71
+ setTimeout ( ( ) => {
72
+ if ( recurse ) startTimer ( )
73
+ } , 1 )
74
+ }
75
+
76
+ startTimer ( )
77
+ await runWaitFor ( { time : 800 } , { timeout : 100 } )
78
+
79
+ recurse = false
80
+ } )
Original file line number Diff line number Diff line change @@ -52,14 +52,25 @@ function waitFor(
52
52
// waiting or when we've timed out.
53
53
// eslint-disable-next-line no-unmodified-loop-condition
54
54
while ( ! finished ) {
55
+ // we *could* (maybe should?) use `advanceTimersToNextTimer` but it's
56
+ // possible that could make this loop go on forever if someone is using
57
+ // third party code that's setting up recursive timers so rapidly that
58
+ // the user's timer's don't get a chance to resolve. So we'll advance
59
+ // by an interval instead. (We have a test for this case).
55
60
jest . advanceTimersByTime ( interval )
56
- // in this rare case, we *need* to wait for in-flight promises
61
+
62
+ // It's really important that checkCallback is run *before* we flush
63
+ // in-flight promises. To be honest, I'm not sure why, and I can't quite
64
+ // think of a way to reproduce the problem in a test, but I spent
65
+ // an entire day banging my head against a wall on this.
66
+ checkCallback ( )
67
+
68
+ // In this rare case, we *need* to wait for in-flight promises
57
69
// to resolve before continuing. We don't need to take advantage
58
70
// of parallelization so we're fine.
59
71
// https://stackoverflow.com/a/59243586/971592
60
72
// eslint-disable-next-line no-await-in-loop
61
73
await new Promise ( r => setImmediate ( r ) )
62
- checkCallback ( )
63
74
}
64
75
} else {
65
76
intervalId = setInterval ( checkCallback , interval )
You can’t perform that action at this time.
0 commit comments