@@ -25,13 +25,23 @@ export default function Backburner(queueNames, options) {
25
25
this . instanceStack = [ ] ;
26
26
this . _debouncees = [ ] ;
27
27
this . _throttlers = [ ] ;
28
- this . _timers = [ ] ;
29
28
this . _eventCallbacks = {
30
29
end : [ ] ,
31
30
begin : [ ]
32
31
} ;
32
+
33
+ this . _timerTimeoutId = undefined ;
34
+ this . _timers = [ ] ;
35
+
36
+ var _this = this ;
37
+ this . _boundRunExpiredTimers = function ( ) {
38
+ _this . _runExpiredTimers ( ) ;
39
+ } ;
33
40
}
34
41
42
+ // ms of delay before we conclude a timeout was lost
43
+ var TIMEOUT_STALLED_THRESHOLD = 1000 ;
44
+
35
45
Backburner . prototype = {
36
46
begin : function ( ) {
37
47
var options = this . options ;
@@ -359,12 +369,27 @@ Backburner.prototype = {
359
369
}
360
370
}
361
371
372
+ return this . _setTimeout ( fn , executeAt ) ;
373
+ } ,
374
+
375
+ _setTimeout ( fn , executeAt ) {
376
+ if ( this . _timers . length === 0 ) {
377
+ this . _timers . push ( executeAt , fn ) ;
378
+ this . _installTimerTimeout ( ) ;
379
+ return fn ;
380
+ }
381
+
382
+ this . _reinstallStalledTimerTimeout ( ) ;
383
+
362
384
// find position to insert
363
385
var i = searchTimer ( executeAt , this . _timers ) ;
364
386
365
387
this . _timers . splice ( i , 0 , executeAt , fn ) ;
366
388
367
- updateLaterTimer ( this , executeAt , wait ) ;
389
+ // we should be the new earliest timer if i == 0
390
+ if ( i === 0 ) {
391
+ this . _reinstallTimerTimeout ( ) ;
392
+ }
368
393
369
394
return fn ;
370
395
} ,
@@ -464,20 +489,17 @@ Backburner.prototype = {
464
489
} ,
465
490
466
491
cancelTimers : function ( ) {
467
- var clearItems = function ( item ) {
492
+ function clearItems ( item ) {
468
493
clearTimeout ( item [ 2 ] ) ;
469
- } ;
494
+ }
470
495
471
496
each ( this . _throttlers , clearItems ) ;
472
497
this . _throttlers = [ ] ;
473
498
474
499
each ( this . _debouncees , clearItems ) ;
475
500
this . _debouncees = [ ] ;
476
501
477
- if ( this . _laterTimer ) {
478
- clearTimeout ( this . _laterTimer ) ;
479
- this . _laterTimer = null ;
480
- }
502
+ this . _clearTimerTimeout ( ) ;
481
503
this . _timers = [ ] ;
482
504
483
505
if ( this . _autorun ) {
@@ -490,7 +512,7 @@ Backburner.prototype = {
490
512
return ! ! this . _timers . length || ! ! this . _debouncees . length || ! ! this . _throttlers . length || this . _autorun ;
491
513
} ,
492
514
493
- cancel : function ( timer ) {
515
+ cancel : function ( timer ) {
494
516
var timerType = typeof timer ;
495
517
496
518
if ( timer && timerType === 'object' && timer . queue && timer . method ) { // we're cancelling a deferOnce
@@ -500,13 +522,7 @@ Backburner.prototype = {
500
522
if ( this . _timers [ i + 1 ] === timer ) {
501
523
this . _timers . splice ( i , 2 ) ; // remove the two elements
502
524
if ( i === 0 ) {
503
- if ( this . _laterTimer ) { // Active timer? Then clear timer and reset for future timer
504
- clearTimeout ( this . _laterTimer ) ;
505
- this . _laterTimer = null ;
506
- }
507
- if ( this . _timers . length > 0 ) { // Update to next available timer when available
508
- updateLaterTimer ( this , this . _timers [ 0 ] , this . _timers [ 0 ] - now ( ) ) ;
509
- }
525
+ this . _reinstallTimerTimeout ( ) ;
510
526
}
511
527
return true ;
512
528
}
@@ -538,6 +554,67 @@ Backburner.prototype = {
538
554
}
539
555
540
556
return false ;
557
+ } ,
558
+
559
+ _runExpiredTimers : function ( ) {
560
+ this . _timerTimeoutId = undefined ;
561
+ this . run ( this , this . _scheduleExpiredTimers ) ;
562
+ } ,
563
+
564
+ _scheduleExpiredTimers : function ( ) {
565
+ var n = now ( ) ;
566
+ var timers = this . _timers ;
567
+ var i = 0 ;
568
+ var l = timers . length ;
569
+ for ( ; i < l ; i += 2 ) {
570
+ var executeAt = timers [ i ] ;
571
+ var fn = timers [ i + 1 ] ;
572
+ if ( executeAt <= n ) {
573
+ this . schedule ( this . options . defaultQueue , null , fn ) ;
574
+ } else {
575
+ break ;
576
+ }
577
+ }
578
+ timers . splice ( 0 , i ) ;
579
+ this . _installTimerTimeout ( ) ;
580
+ } ,
581
+
582
+ _reinstallStalledTimerTimeout : function ( ) {
583
+ if ( ! this . _timerTimeoutId ) {
584
+ return ;
585
+ }
586
+ // if we have a timer we should always have a this._timerTimeoutId
587
+ var minExpiresAt = this . _timers [ 0 ] ;
588
+ var delay = now ( ) - minExpiresAt ;
589
+ // threshold of a second before we assume that the currently
590
+ // installed timeout will not run, so we don't constantly reinstall
591
+ // timeouts that are delayed but good still
592
+ if ( delay < TIMEOUT_STALLED_THRESHOLD ) {
593
+ return ;
594
+ }
595
+ } ,
596
+
597
+ _reinstallTimerTimeout : function ( ) {
598
+ this . _clearTimerTimeout ( ) ;
599
+ this . _installTimerTimeout ( ) ;
600
+ } ,
601
+
602
+ _clearTimerTimeout : function ( ) {
603
+ if ( ! this . _timerTimeoutId ) {
604
+ return ;
605
+ }
606
+ clearTimeout ( this . _timerTimeoutId ) ;
607
+ this . _timerTimeoutId = undefined ;
608
+ } ,
609
+
610
+ _installTimerTimeout : function ( ) {
611
+ if ( ! this . _timers . length ) {
612
+ return ;
613
+ }
614
+ var minExpiresAt = this . _timers [ 0 ] ;
615
+ var n = now ( ) ;
616
+ var wait = Math . max ( 0 , minExpiresAt - n ) ;
617
+ this . _timerTimeoutId = setTimeout ( this . _boundRunExpiredTimers , wait ) ;
541
618
}
542
619
} ;
543
620
@@ -565,51 +642,6 @@ function createAutorun(backburner) {
565
642
} ) ;
566
643
}
567
644
568
- function updateLaterTimer ( backburner , executeAt , wait ) {
569
- var n = now ( ) ;
570
- if ( ! backburner . _laterTimer || executeAt < backburner . _laterTimerExpiresAt || backburner . _laterTimerExpiresAt < n ) {
571
-
572
- if ( backburner . _laterTimer ) {
573
- // Clear when:
574
- // - Already expired
575
- // - New timer is earlier
576
- clearTimeout ( backburner . _laterTimer ) ;
577
-
578
- if ( backburner . _laterTimerExpiresAt < n ) { // If timer was never triggered
579
- // Calculate the left-over wait-time
580
- wait = Math . max ( 0 , executeAt - n ) ;
581
- }
582
- }
583
-
584
- backburner . _laterTimer = platform . setTimeout ( function ( ) {
585
- backburner . _laterTimer = null ;
586
- backburner . _laterTimerExpiresAt = null ;
587
- executeTimers ( backburner ) ;
588
- } , wait ) ;
589
-
590
- backburner . _laterTimerExpiresAt = n + wait ;
591
- }
592
- }
593
-
594
- function executeTimers ( backburner ) {
595
- var n = now ( ) ;
596
- var fns , i , l ;
597
-
598
- backburner . run ( function ( ) {
599
- i = searchTimer ( n , backburner . _timers ) ;
600
-
601
- fns = backburner . _timers . splice ( 0 , i ) ;
602
-
603
- for ( i = 1 , l = fns . length ; i < l ; i += 2 ) {
604
- backburner . schedule ( backburner . options . defaultQueue , null , fns [ i ] ) ;
605
- }
606
- } ) ;
607
-
608
- if ( backburner . _timers . length ) {
609
- updateLaterTimer ( backburner , backburner . _timers [ 0 ] , backburner . _timers [ 0 ] - n ) ;
610
- }
611
- }
612
-
613
645
function findDebouncee ( target , method , debouncees ) {
614
646
return findItem ( target , method , debouncees ) ;
615
647
}
0 commit comments