71
71
function $RootScopeProvider ( ) {
72
72
var TTL = 10 ;
73
73
var $rootScopeMinErr = minErr ( '$rootScope' ) ;
74
+ var lastDirtyWatch = null ;
74
75
75
76
this . digestTtl = function ( value ) {
76
77
if ( arguments . length ) {
@@ -325,6 +326,8 @@ function $RootScopeProvider(){
325
326
eq : ! ! objectEquality
326
327
} ;
327
328
329
+ lastDirtyWatch = null ;
330
+
328
331
// in the case user pass string, we need to compile it, do we really need this ?
329
332
if ( ! isFunction ( listener ) ) {
330
333
var listenFn = compileToFn ( listener || noop , 'listener' ) ;
@@ -553,6 +556,8 @@ function $RootScopeProvider(){
553
556
554
557
beginPhase ( '$digest' ) ;
555
558
559
+ lastDirtyWatch = null ;
560
+
556
561
do { // "while dirty" loop
557
562
dirty = false ;
558
563
current = target ;
@@ -565,8 +570,10 @@ function $RootScopeProvider(){
565
570
clearPhase ( ) ;
566
571
$exceptionHandler ( e ) ;
567
572
}
573
+ lastDirtyWatch = null ;
568
574
}
569
575
576
+ traverseScopesLoop:
570
577
do { // "traverse the scopes" loop
571
578
if ( ( watchers = current . $$watchers ) ) {
572
579
// process our watches
@@ -576,22 +583,30 @@ function $RootScopeProvider(){
576
583
watch = watchers [ length ] ;
577
584
// Most common watches are on primitives, in which case we can short
578
585
// circuit it with === operator, only when === fails do we use .equals
579
- if ( watch && ( value = watch . get ( current ) ) !== ( last = watch . last ) &&
580
- ! ( watch . eq
581
- ? equals ( value , last )
582
- : ( typeof value == 'number' && typeof last == 'number'
583
- && isNaN ( value ) && isNaN ( last ) ) ) ) {
584
- dirty = true ;
585
- watch . last = watch . eq ? copy ( value ) : value ;
586
- watch . fn ( value , ( ( last === initWatchVal ) ? value : last ) , current ) ;
587
- if ( ttl < 5 ) {
588
- logIdx = 4 - ttl ;
589
- if ( ! watchLog [ logIdx ] ) watchLog [ logIdx ] = [ ] ;
590
- logMsg = ( isFunction ( watch . exp ) )
591
- ? 'fn: ' + ( watch . exp . name || watch . exp . toString ( ) )
592
- : watch . exp ;
593
- logMsg += '; newVal: ' + toJson ( value ) + '; oldVal: ' + toJson ( last ) ;
594
- watchLog [ logIdx ] . push ( logMsg ) ;
586
+ if ( watch ) {
587
+ if ( ( value = watch . get ( current ) ) !== ( last = watch . last ) &&
588
+ ! ( watch . eq
589
+ ? equals ( value , last )
590
+ : ( typeof value == 'number' && typeof last == 'number'
591
+ && isNaN ( value ) && isNaN ( last ) ) ) ) {
592
+ dirty = true ;
593
+ lastDirtyWatch = watch ;
594
+ watch . last = watch . eq ? copy ( value ) : value ;
595
+ watch . fn ( value , ( ( last === initWatchVal ) ? value : last ) , current ) ;
596
+ if ( ttl < 5 ) {
597
+ logIdx = 4 - ttl ;
598
+ if ( ! watchLog [ logIdx ] ) watchLog [ logIdx ] = [ ] ;
599
+ logMsg = ( isFunction ( watch . exp ) )
600
+ ? 'fn: ' + ( watch . exp . name || watch . exp . toString ( ) )
601
+ : watch . exp ;
602
+ logMsg += '; newVal: ' + toJson ( value ) + '; oldVal: ' + toJson ( last ) ;
603
+ watchLog [ logIdx ] . push ( logMsg ) ;
604
+ }
605
+ } else if ( watch === lastDirtyWatch ) {
606
+ // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
607
+ // have already been tested.
608
+ dirty = false ;
609
+ break traverseScopesLoop;
595
610
}
596
611
}
597
612
} catch ( e ) {
@@ -604,20 +619,24 @@ function $RootScopeProvider(){
604
619
// Insanity Warning: scope depth-first traversal
605
620
// yes, this code is a bit crazy, but it works and we have tests to prove it!
606
621
// this piece should be kept in sync with the traversal in $broadcast
607
- if ( ! ( next = ( current . $$childHead || ( current !== target && current . $$nextSibling ) ) ) ) {
622
+ if ( ! ( next = ( current . $$childHead ||
623
+ ( current !== target && current . $$nextSibling ) ) ) ) {
608
624
while ( current !== target && ! ( next = current . $$nextSibling ) ) {
609
625
current = current . $parent ;
610
626
}
611
627
}
612
628
} while ( ( current = next ) ) ;
613
629
630
+ // `break traverseScopesLoop;` takes us to here
631
+
614
632
if ( dirty && ! ( ttl -- ) ) {
615
633
clearPhase ( ) ;
616
634
throw $rootScopeMinErr ( 'infdig' ,
617
635
'{0} $digest() iterations reached. Aborting!\n' +
618
636
'Watchers fired in the last 5 iterations: {1}' ,
619
637
TTL , toJson ( watchLog ) ) ;
620
638
}
639
+
621
640
} while ( dirty || asyncQueue . length ) ;
622
641
623
642
clearPhase ( ) ;
0 commit comments