@@ -254,16 +254,17 @@ function processTimers(now) {
254
254
debug ( 'process timer lists %d' , now ) ;
255
255
nextExpiry = Infinity ;
256
256
257
- let list , ran ;
257
+ let list ;
258
+ let ranAtLeastOneList = false ;
258
259
while ( list = queue . peek ( ) ) {
259
260
if ( list . expiry > now ) {
260
261
nextExpiry = list . expiry ;
261
262
return refCount > 0 ? nextExpiry : - nextExpiry ;
262
263
}
263
- if ( ran )
264
+ if ( ranAtLeastOneList )
264
265
runNextTicks ( ) ;
265
266
else
266
- ran = true ;
267
+ ranAtLeastOneList = true ;
267
268
listOnTimeout ( list , now ) ;
268
269
}
269
270
return 0 ;
@@ -275,6 +276,7 @@ function listOnTimeout(list, now) {
275
276
debug ( 'timeout callback %d' , msecs ) ;
276
277
277
278
var diff , timer ;
279
+ let ranAtLeastOneTimer = false ;
278
280
while ( timer = L . peek ( list ) ) {
279
281
diff = now - timer . _idleStart ;
280
282
@@ -288,6 +290,11 @@ function listOnTimeout(list, now) {
288
290
return ;
289
291
}
290
292
293
+ if ( ranAtLeastOneTimer )
294
+ runNextTicks ( ) ;
295
+ else
296
+ ranAtLeastOneTimer = true ;
297
+
291
298
// The actual logic for when a timeout happens.
292
299
L . remove ( timer ) ;
293
300
@@ -307,7 +314,33 @@ function listOnTimeout(list, now) {
307
314
308
315
emitBefore ( asyncId , timer [ trigger_async_id_symbol ] ) ;
309
316
310
- tryOnTimeout ( timer ) ;
317
+ let start ;
318
+ if ( timer . _repeat )
319
+ start = getLibuvNow ( ) ;
320
+
321
+ try {
322
+ const args = timer . _timerArgs ;
323
+ if ( ! args )
324
+ timer . _onTimeout ( ) ;
325
+ else
326
+ Reflect . apply ( timer . _onTimeout , timer , args ) ;
327
+ } finally {
328
+ if ( timer . _repeat && timer . _idleTimeout !== - 1 ) {
329
+ timer . _idleTimeout = timer . _repeat ;
330
+ if ( start === undefined )
331
+ start = getLibuvNow ( ) ;
332
+ insert ( timer , timer [ kRefed ] , start ) ;
333
+ } else {
334
+ if ( timer [ kRefed ] )
335
+ refCount -- ;
336
+ timer [ kRefed ] = null ;
337
+
338
+ if ( destroyHooksExist ( ) && ! timer . _destroyed ) {
339
+ emitDestroy ( timer [ async_id_symbol ] ) ;
340
+ timer . _destroyed = true ;
341
+ }
342
+ }
343
+ }
311
344
312
345
emitAfter ( asyncId ) ;
313
346
}
@@ -327,30 +360,6 @@ function listOnTimeout(list, now) {
327
360
}
328
361
329
362
330
- // An optimization so that the try/finally only de-optimizes (since at least v8
331
- // 4.7) what is in this smaller function.
332
- function tryOnTimeout ( timer , start ) {
333
- if ( start === undefined && timer . _repeat )
334
- start = getLibuvNow ( ) ;
335
- try {
336
- ontimeout ( timer ) ;
337
- } finally {
338
- if ( timer . _repeat ) {
339
- rearm ( timer , start ) ;
340
- } else {
341
- if ( timer [ kRefed ] )
342
- refCount -- ;
343
- timer [ kRefed ] = null ;
344
-
345
- if ( destroyHooksExist ( ) && ! timer . _destroyed ) {
346
- emitDestroy ( timer [ async_id_symbol ] ) ;
347
- timer . _destroyed = true ;
348
- }
349
- }
350
- }
351
- }
352
-
353
-
354
363
// Remove a timer. Cancels the timeout and resets the relevant timer properties.
355
364
function unenroll ( item ) {
356
365
// Fewer checks may be possible, but these cover everything.
@@ -456,26 +465,6 @@ setTimeout[internalUtil.promisify.custom] = function(after, value) {
456
465
exports . setTimeout = setTimeout ;
457
466
458
467
459
- function ontimeout ( timer ) {
460
- const args = timer . _timerArgs ;
461
- if ( typeof timer . _onTimeout !== 'function' )
462
- return Promise . resolve ( timer . _onTimeout , args [ 0 ] ) ;
463
- if ( ! args )
464
- timer . _onTimeout ( ) ;
465
- else
466
- Reflect . apply ( timer . _onTimeout , timer , args ) ;
467
- }
468
-
469
- function rearm ( timer , start ) {
470
- // Do not re-arm unenroll'd or closed timers.
471
- if ( timer . _idleTimeout === - 1 )
472
- return ;
473
-
474
- timer . _idleTimeout = timer . _repeat ;
475
- insert ( timer , timer [ kRefed ] , start ) ;
476
- }
477
-
478
-
479
468
const clearTimeout = exports . clearTimeout = function clearTimeout ( timer ) {
480
469
if ( timer && timer . _onTimeout ) {
481
470
timer . _onTimeout = null ;
@@ -601,75 +590,63 @@ function processImmediate() {
601
590
const queue = outstandingQueue . head !== null ?
602
591
outstandingQueue : immediateQueue ;
603
592
var immediate = queue . head ;
604
- const tail = queue . tail ;
605
593
606
594
// Clear the linked list early in case new `setImmediate()` calls occur while
607
595
// immediate callbacks are executed
608
- queue . head = queue . tail = null ;
609
-
610
- let count = 0 ;
611
- let refCount = 0 ;
596
+ if ( queue !== outstandingQueue ) {
597
+ queue . head = queue . tail = null ;
598
+ immediateInfo [ kHasOutstanding ] = 1 ;
599
+ }
612
600
601
+ let prevImmediate ;
602
+ let ranAtLeastOneImmediate = false ;
613
603
while ( immediate !== null ) {
614
- immediate . _destroyed = true ;
604
+ if ( ranAtLeastOneImmediate )
605
+ runNextTicks ( ) ;
606
+ else
607
+ ranAtLeastOneImmediate = true ;
615
608
616
- const asyncId = immediate [ async_id_symbol ] ;
617
- emitBefore ( asyncId , immediate [ trigger_async_id_symbol ] ) ;
609
+ // It's possible for this current Immediate to be cleared while executing
610
+ // the next tick queue above, which means we need to use the previous
611
+ // Immediate's _idleNext which is guaranteed to not have been cleared.
612
+ if ( immediate . _destroyed ) {
613
+ outstandingQueue . head = immediate = prevImmediate . _idleNext ;
614
+ continue ;
615
+ }
618
616
619
- count ++ ;
617
+ immediate . _destroyed = true ;
618
+
619
+ immediateInfo [ kCount ] -- ;
620
620
if ( immediate [ kRefed ] )
621
- refCount ++ ;
621
+ immediateInfo [ kRefCount ] -- ;
622
622
immediate [ kRefed ] = null ;
623
623
624
- tryOnImmediate ( immediate , tail , count , refCount ) ;
624
+ prevImmediate = immediate ;
625
625
626
- emitAfter ( asyncId ) ;
626
+ const asyncId = immediate [ async_id_symbol ] ;
627
+ emitBefore ( asyncId , immediate [ trigger_async_id_symbol ] ) ;
627
628
628
- immediate = immediate . _idleNext ;
629
- }
629
+ try {
630
+ const argv = immediate . _argv ;
631
+ if ( ! argv )
632
+ immediate . _onImmediate ( ) ;
633
+ else
634
+ Reflect . apply ( immediate . _onImmediate , immediate , argv ) ;
635
+ } finally {
636
+ immediate . _onImmediate = null ;
630
637
631
- immediateInfo [ kCount ] -= count ;
632
- immediateInfo [ kRefCount ] -= refCount ;
633
- immediateInfo [ kHasOutstanding ] = 0 ;
634
- }
638
+ if ( destroyHooksExist ( ) )
639
+ emitDestroy ( asyncId ) ;
635
640
636
- // An optimization so that the try/finally only de-optimizes (since at least v8
637
- // 4.7) what is in this smaller function.
638
- function tryOnImmediate ( immediate , oldTail , count , refCount ) {
639
- var threw = true ;
640
- try {
641
- // make the actual call outside the try/finally to allow it to be optimized
642
- runCallback ( immediate ) ;
643
- threw = false ;
644
- } finally {
645
- immediate . _onImmediate = null ;
646
-
647
- if ( destroyHooksExist ( ) ) {
648
- emitDestroy ( immediate [ async_id_symbol ] ) ;
641
+ outstandingQueue . head = immediate = immediate . _idleNext ;
649
642
}
650
643
651
- if ( threw ) {
652
- immediateInfo [ kCount ] -= count ;
653
- immediateInfo [ kRefCount ] -= refCount ;
654
-
655
- if ( immediate . _idleNext !== null ) {
656
- // Handle any remaining Immediates after error handling has resolved,
657
- // assuming we're still alive to do so.
658
- outstandingQueue . head = immediate . _idleNext ;
659
- outstandingQueue . tail = oldTail ;
660
- immediateInfo [ kHasOutstanding ] = 1 ;
661
- }
662
- }
644
+ emitAfter ( asyncId ) ;
663
645
}
664
- }
665
646
666
- function runCallback ( timer ) {
667
- const argv = timer . _argv ;
668
- if ( typeof timer . _onImmediate !== 'function' )
669
- return Promise . resolve ( timer . _onImmediate , argv [ 0 ] ) ;
670
- if ( ! argv )
671
- return timer . _onImmediate ( ) ;
672
- Reflect . apply ( timer . _onImmediate , timer , argv ) ;
647
+ if ( queue === outstandingQueue )
648
+ outstandingQueue . head = null ;
649
+ immediateInfo [ kHasOutstanding ] = 0 ;
673
650
}
674
651
675
652
0 commit comments