@@ -13,13 +13,28 @@ import {normalizeBlank, isPresent, global} from 'angular2/src/facade/lang';
1313 * @exportedAs angular2/core
1414 */
1515export class VmTurnZone {
16+ // Code executed in _outerZone does not trigger the onTurnDone.
1617 _outerZone ;
18+ // _innerZone is the child of _outerZone. Any code executed in this zone will trigger the
19+ // onTurnDone hook at the end of the current VM turn.
1720 _innerZone ;
1821
1922 _onTurnStart :Function ;
2023 _onTurnDone :Function ;
2124 _onErrorHandler :Function ;
2225
26+ // Number of microtasks pending from _outerZone (& descendants)
27+ _pendingMicrotask : number ;
28+ // Whether some code has been executed in the _innerZone (& descendants) in the current turn
29+ _hasExecutedCodeInInnerZone : boolean ;
30+ // Whether the onTurnStart hook is executing
31+ _inTurnStart : boolean ;
32+ // run() call depth in _outerZone. 0 at the end of a macrotask
33+ // zone.run(() => { // top-level call
34+ // zone.run(() => {}); // nested call -> in-turn
35+ // });
36+ _nestedRun : number ;
37+
2338 /**
2439 * Associates with this
2540 *
@@ -33,17 +48,14 @@ export class VmTurnZone {
3348 this . _onTurnStart = null ;
3449 this . _onTurnDone = null ;
3550 this . _onErrorHandler = null ;
36- Zone . _hasExecutedInnerCode = false ;
3751
38- this . _outerZone = global . zone . fork ( {
39- _name : 'outer' ,
40- beforeTurn : ( ) => { this . _beforeTurn ( ) ; } ,
41- afterTurn : ( ) => { this . _afterTurn ( ) ; }
42- } ) ;
43- this . _innerZone = this . _createInnerZone ( this . _outerZone , enableLongStackTrace ) ;
52+ this . _pendingMicrotasks = 0 ;
53+ this . _hasExecutedCodeInInnerZone = false ;
54+ this . _inTurnStart = false ;
55+ this . _nestedRun = 0 ;
4456
45- // TODO('remove' );
46- Zone . debug = true ;
57+ this . _outerZone = this . _createOuterZone ( global . zone ) ;
58+ this . _innerZone = this . _createInnerZone ( this . _outerZone , enableLongStackTrace ) ;
4759 }
4860
4961 /**
@@ -97,6 +109,50 @@ export class VmTurnZone {
97109 return this . _outerZone . run ( fn ) ;
98110 }
99111
112+ _createOuterZone ( zone ) {
113+ var vmTurnZone = this ;
114+
115+ return zone . fork ( {
116+ _name : 'outer' ,
117+ '$run' : function ( parentRun ) {
118+ return function ( ) {
119+ try {
120+ vmTurnZone . _nestedRun ++ ;
121+ return parentRun . apply ( this , arguments ) ;
122+ } finally {
123+ vmTurnZone . _nestedRun -- ;
124+ // If there are no more pending microtasks, we are at the end of a VM turn (or in onTurnStart)
125+ // _nestedRun will be 0 at the end of a macrotasks (it could be > 0 when there are nested calls
126+ // to run()).
127+ if ( vmTurnZone . _pendingMicrotasks == 0 && vmTurnZone . _nestedRun == 0 ) {
128+ if ( vmTurnZone . _onTurnDone && ! vmTurnZone . _inTurnStart && vmTurnZone . _hasExecutedCodeInInnerZone ) {
129+ try {
130+ parentRun . call ( vmTurnZone . _innerZone , vmTurnZone . _onTurnDone ) ;
131+ } finally {
132+ vmTurnZone . _hasExecutedCodeInInnerZone = false ;
133+ }
134+ }
135+ }
136+ vmTurnZone . _inTurnStart = false ;
137+ }
138+ }
139+ } ,
140+ '$scheduleMicrotask' : function ( parentScheduleMicrotask ) {
141+ return function ( fn ) {
142+ vmTurnZone . _pendingMicrotasks ++ ;
143+ var microtask = function ( ) {
144+ try {
145+ fn ( ) ;
146+ } finally {
147+ vmTurnZone . _pendingMicrotasks -- ;
148+ }
149+ } ;
150+ parentScheduleMicrotask . call ( this , microtask ) ;
151+ }
152+ }
153+ } ) ;
154+ }
155+
100156 _createInnerZone ( zone , enableLongStackTrace ) {
101157 var vmTurnZone = this ;
102158 var errorHandling ;
@@ -118,40 +174,29 @@ export class VmTurnZone {
118174 return zone
119175 . fork ( errorHandling )
120176 . fork ( {
177+ // Executes code in the _innerZone & trigger the onTurnStart hook when code is executed for the
178+ // first time in a turn.
121179 '$run' : function ( parentRun ) {
122180 return function ( ) {
123- if ( ! Zone . _hasExecutedInnerCode ) {
124- // Execute the beforeTurn hook when code is first executed in the inner zone in the turn
125- Zone . _hasExecutedInnerCode = true ;
126- var oldZone = global . zone ;
127- global . zone = this ;
128- this . beforeTurn ( ) ;
129- global . zone = oldZone ;
130- }
131-
132- return parentRun . apply ( this , arguments ) ;
181+ vmTurnZone . _maybeStartVmTurn ( )
182+ return parentRun . apply ( this , arguments )
133183 }
134184 } ,
135185 _name : 'inner'
136186 } ) ;
137187 }
138188
139- _beforeTurn ( ) {
140- this . _onTurnStart && this . _onTurnStart ( ) ;
141- }
142-
143- _afterTurn ( ) {
144- if ( this . _onTurnDone ) {
145- if ( Zone . _hasExecutedInnerCode ) {
146- // Execute the onTurnDone hook in the inner zone so that microtasks are enqueued there
147- // The hook gets executed when code has runned in the inner zone during the current turn
148- this . _innerZone . run ( this . _onTurnDone , this ) ;
149- Zone . _hasExecutedInnerCode = false ;
189+ _maybeStartVmTurn ( ) : void {
190+ if ( ! this . _hasExecutedCodeInInnerZone ) {
191+ this . _hasExecutedCodeInInnerZone = true ;
192+ if ( this . _onTurnStart ) {
193+ this . _inTurnStart = true ;
194+ this . _outerZone . run . call ( this . _innerZone , this . _onTurnStart ) ;
150195 }
151196 }
152197 }
153198
154- _onError ( zone , e ) {
199+ _onError ( zone , e ) : void {
155200 if ( isPresent ( this . _onErrorHandler ) ) {
156201 var trace = [ normalizeBlank ( e . stack ) ] ;
157202
@@ -161,6 +206,8 @@ export class VmTurnZone {
161206 }
162207 this . _onErrorHandler ( e , trace ) ;
163208 } else {
209+ console . log ( '## _onError ##' ) ;
210+ console . log ( e . stack ) ;
164211 throw e ;
165212 }
166213 }
0 commit comments