@@ -15,10 +15,12 @@ const timerPromises = require('timers/promises');
15
15
16
16
const setTimeout = promisify ( timers . setTimeout ) ;
17
17
const setImmediate = promisify ( timers . setImmediate ) ;
18
+ const setInterval = promisify ( timers . setInterval ) ;
18
19
const exec = promisify ( child_process . exec ) ;
19
20
20
21
assert . strictEqual ( setTimeout , timerPromises . setTimeout ) ;
21
22
assert . strictEqual ( setImmediate , timerPromises . setImmediate ) ;
23
+ assert . strictEqual ( setInterval , timerPromises . setInterval ) ;
22
24
23
25
process . on ( 'multipleResolves' , common . mustNotCall ( ) ) ;
24
26
@@ -50,6 +52,66 @@ process.on('multipleResolves', common.mustNotCall());
50
52
} ) ) ;
51
53
}
52
54
55
+ {
56
+ const controller = new AbortController ( ) ;
57
+ const { signal } = controller ;
58
+ const iterable = setInterval ( 1 , undefined , { signal } ) ;
59
+ const iterator = iterable [ Symbol . asyncIterator ] ( ) ;
60
+ const promise = iterator . next ( ) ;
61
+ promise . then ( common . mustCall ( ( result ) => {
62
+ assert . ok ( ! result . done ) ;
63
+ assert . strictEqual ( result . value , undefined ) ;
64
+ controller . abort ( ) ;
65
+ return assert . rejects ( iterator . next ( ) , / A b o r t E r r o r / ) ;
66
+ } ) ) ;
67
+ }
68
+
69
+ {
70
+ const controller = new AbortController ( ) ;
71
+ const { signal } = controller ;
72
+ const iterable = setInterval ( 1 , undefined , { signal, throwOnAbort : false } ) ;
73
+ const iterator = iterable [ Symbol . asyncIterator ] ( ) ;
74
+ const promise = iterator . next ( ) ;
75
+ promise . then ( common . mustCall ( ( result ) => {
76
+ assert . ok ( ! result . done ) ;
77
+ assert . strictEqual ( result . value , undefined ) ;
78
+ controller . abort ( ) ;
79
+ return iterator . next ( ) ;
80
+ } ) ) . then ( common . mustCall ( ( result ) => {
81
+ assert . ok ( result . done ) ;
82
+ } ) ) ;
83
+ }
84
+
85
+ {
86
+ const controller = new AbortController ( ) ;
87
+ const { signal } = controller ;
88
+ const iterable = setInterval ( 1 , 'foobar' , { signal } ) ;
89
+ const iterator = iterable [ Symbol . asyncIterator ] ( ) ;
90
+ const promise = iterator . next ( ) ;
91
+ promise . then ( common . mustCall ( ( result ) => {
92
+ assert . ok ( ! result . done ) ;
93
+ assert . strictEqual ( result . value , 'foobar' ) ;
94
+ controller . abort ( ) ;
95
+ return assert . rejects ( iterator . next ( ) , / A b o r t E r r o r / ) ;
96
+ } ) ) ;
97
+ }
98
+
99
+ {
100
+ const controller = new AbortController ( ) ;
101
+ const { signal } = controller ;
102
+ const iterable = setInterval ( 1 , 'foobar' , { signal, throwOnAbort : false } ) ;
103
+ const iterator = iterable [ Symbol . asyncIterator ] ( ) ;
104
+ const promise = iterator . next ( ) ;
105
+ promise . then ( common . mustCall ( ( result ) => {
106
+ assert . ok ( ! result . done ) ;
107
+ assert . strictEqual ( result . value , 'foobar' ) ;
108
+ controller . abort ( ) ;
109
+ return iterator . next ( ) ;
110
+ } ) ) . then ( common . mustCall ( ( result ) => {
111
+ assert . ok ( result . done ) ;
112
+ } ) ) ;
113
+ }
114
+
53
115
{
54
116
const ac = new AbortController ( ) ;
55
117
const signal = ac . signal ;
@@ -78,6 +140,33 @@ process.on('multipleResolves', common.mustNotCall());
78
140
assert . rejects ( setImmediate ( 10 , { signal } ) , / A b o r t E r r o r / ) ;
79
141
}
80
142
143
+ {
144
+ const ac = new AbortController ( ) ;
145
+ const { signal } = ac ;
146
+ ac . abort ( ) ; // Abort in advance
147
+
148
+ const iterable = setInterval ( 1 , undefined , { signal } ) ;
149
+ const iterator = iterable [ Symbol . asyncIterator ] ( ) ;
150
+
151
+ assert . rejects ( iterator . next ( ) , / A b o r t E r r o r / ) ;
152
+ }
153
+
154
+ {
155
+ const ac = new AbortController ( ) ;
156
+ const { signal } = ac ;
157
+
158
+ const iterable = setInterval ( 100 , undefined , { signal } ) ;
159
+ const iterator = iterable [ Symbol . asyncIterator ] ( ) ;
160
+
161
+ // This promise should take 100 seconds to resolve, so now aborting it should
162
+ // mean we abort early
163
+ const promise = iterator . next ( ) ;
164
+
165
+ ac . abort ( ) ; // Abort in after we have a next promise
166
+
167
+ assert . rejects ( promise , / A b o r t E r r o r / ) ;
168
+ }
169
+
81
170
{
82
171
// Check that aborting after resolve will not reject.
83
172
const ac = new AbortController ( ) ;
@@ -95,6 +184,23 @@ process.on('multipleResolves', common.mustNotCall());
95
184
} ) ;
96
185
}
97
186
187
+ {
188
+ [ 1 , '' , Infinity , null , { } ] . forEach ( ( ref ) => {
189
+ const iterable = setInterval ( 10 , undefined , { ref } ) ;
190
+ assert . rejects ( ( ) => iterable [ Symbol . asyncIterator ] ( ) . next ( ) , / E R R _ I N V A L I D _ A R G _ T Y P E / ) ;
191
+ } ) ;
192
+
193
+ [ 1 , '' , Infinity , null , { } ] . forEach ( ( signal ) => {
194
+ const iterable = setInterval ( 10 , undefined , { signal } ) ;
195
+ assert . rejects ( ( ) => iterable [ Symbol . asyncIterator ] ( ) . next ( ) , / E R R _ I N V A L I D _ A R G _ T Y P E / ) ;
196
+ } ) ;
197
+
198
+ [ 1 , '' , Infinity , null , true , false ] . forEach ( ( options ) => {
199
+ const iterable = setInterval ( 10 , undefined , options ) ;
200
+ assert . rejects ( ( ) => iterable [ Symbol . asyncIterator ] ( ) . next ( ) , / E R R _ I N V A L I D _ A R G _ T Y P E / ) ;
201
+ } ) ;
202
+ }
203
+
98
204
{
99
205
// Check that timer adding signals does not leak handlers
100
206
const signal = new NodeEventTarget ( ) ;
@@ -165,3 +271,73 @@ process.on('multipleResolves', common.mustNotCall());
165
271
assert . strictEqual ( stderr , '' ) ;
166
272
} ) ) ;
167
273
}
274
+
275
+ {
276
+ exec ( `${ process . execPath } -pe "const assert = require('assert');` +
277
+ 'const interval = require(\'timers/promises\')' +
278
+ '.setInterval(1000, null, { ref: false });' +
279
+ 'interval[Symbol.asyncIterator]().next()' +
280
+ '.then(assert.fail)"' ) . then ( common . mustCall ( ( { stderr } ) => {
281
+ assert . strictEqual ( stderr , '' ) ;
282
+ } ) ) ;
283
+ }
284
+
285
+ {
286
+ async function runInterval ( fn , intervalTime , signal ) {
287
+ const input = 'foobar' ;
288
+ const interval = setInterval ( intervalTime , input , { signal } ) ;
289
+ let iteration = 0 ;
290
+ for await ( const value of interval ) {
291
+ const time = Date . now ( ) ;
292
+ assert . strictEqual ( value , input ) ;
293
+ await fn ( time , iteration ) ;
294
+ iteration ++ ;
295
+ }
296
+ }
297
+
298
+ {
299
+ const controller = new AbortController ( ) ;
300
+ const { signal } = controller ;
301
+
302
+ let prevTime ;
303
+ let looped = 0 ;
304
+ const delay = 20 ;
305
+ const timeoutLoop = runInterval ( ( time ) => {
306
+ looped ++ ;
307
+ if ( looped === 5 ) controller . abort ( ) ;
308
+ if ( looped > 5 ) throw new Error ( 'ran too many times' ) ;
309
+ if ( prevTime && time - prevTime < delay ) {
310
+ const diff = time - prevTime ;
311
+ throw new Error ( `${ diff } between iterations, lower than ${ delay } ` ) ;
312
+ }
313
+ prevTime = time ;
314
+ } , delay , signal ) ;
315
+
316
+ assert . rejects ( timeoutLoop , / A b o r t E r r o r / ) ;
317
+ timeoutLoop . catch ( common . mustCall ( ( ) => {
318
+ assert . strictEqual ( 5 , looped ) ;
319
+ } ) ) ;
320
+ }
321
+
322
+ {
323
+ // Check that if we abort when we have some callbacks left,
324
+ // we actually call them.
325
+ const controller = new AbortController ( ) ;
326
+ const { signal } = controller ;
327
+ const delay = 10 ;
328
+ let totalIterations = 0 ;
329
+ const timeoutLoop = runInterval ( async ( time , iterationNumber ) => {
330
+ if ( iterationNumber === 1 ) {
331
+ await setTimeout ( delay * 3 ) ;
332
+ controller . abort ( ) ;
333
+ }
334
+ if ( iterationNumber > totalIterations ) {
335
+ totalIterations = iterationNumber ;
336
+ }
337
+ } , delay , signal ) ;
338
+
339
+ timeoutLoop . catch ( common . mustCall ( ( ) => {
340
+ assert . ok ( totalIterations >= 3 ) ;
341
+ } ) ) ;
342
+ }
343
+ }
0 commit comments