@@ -15,10 +15,12 @@ const timerPromises = require('timers/promises');
1515
1616const setTimeout = promisify ( timers . setTimeout ) ;
1717const setImmediate = promisify ( timers . setImmediate ) ;
18+ const setInterval = promisify ( timers . setInterval ) ;
1819const exec = promisify ( child_process . exec ) ;
1920
2021assert . strictEqual ( setTimeout , timerPromises . setTimeout ) ;
2122assert . strictEqual ( setImmediate , timerPromises . setImmediate ) ;
23+ assert . strictEqual ( setInterval , timerPromises . setInterval ) ;
2224
2325process . on ( 'multipleResolves' , common . mustNotCall ( ) ) ;
2426
@@ -50,6 +52,66 @@ process.on('multipleResolves', common.mustNotCall());
5052 } ) ) ;
5153}
5254
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+
53115{
54116 const ac = new AbortController ( ) ;
55117 const signal = ac . signal ;
@@ -78,6 +140,33 @@ process.on('multipleResolves', common.mustNotCall());
78140 assert . rejects ( setImmediate ( 10 , { signal } ) , / A b o r t E r r o r / ) ;
79141}
80142
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+
81170{
82171 // Check that aborting after resolve will not reject.
83172 const ac = new AbortController ( ) ;
@@ -95,6 +184,23 @@ process.on('multipleResolves', common.mustNotCall());
95184 } ) ;
96185}
97186
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+
98204{
99205 // Check that timer adding signals does not leak handlers
100206 const signal = new NodeEventTarget ( ) ;
@@ -165,3 +271,73 @@ process.on('multipleResolves', common.mustNotCall());
165271 assert . strictEqual ( stderr , '' ) ;
166272 } ) ) ;
167273}
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