X Tutup
Skip to content

Commit fd1d60f

Browse files
committed
refactor(VmTurnZone): use the browser microtask queue for JS
1 parent e8a6c95 commit fd1d60f

File tree

10 files changed

+1212
-946
lines changed

10 files changed

+1212
-946
lines changed

karma-js.conf.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ module.exports = function(config) {
1212
// Loaded through the es6-module-loader, in `test-main.js`.
1313
{pattern: 'dist/js/dev/es5/**', included: false, watched: false},
1414

15-
// Promise monkey patch & zone should be included first
16-
'zone/es6-promise.js',
17-
'zone/zone.js',
18-
'zone/inner-zone.js',
15+
'zone/zone-microtask.js',
1916
'zone/long-stack-trace-zone.js',
2017

2118
'node_modules/traceur/bin/traceur-runtime.js',

modules/angular2/src/core/zone/vm_turn_zone.dart

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -55,28 +55,13 @@ class VmTurnZone {
5555
// is exhausted and code has been executed in the _innerZone.
5656
if (enableLongStackTrace) {
5757
_outerZone = Chain.capture(
58-
() {
59-
return Zone.current.fork(
60-
specification: new ZoneSpecification(
61-
scheduleMicrotask: _scheduleMicrotask,
62-
run: _outerRun,
63-
runUnary: _outerRunUnary,
64-
runBinary: _outerRunBinary
65-
),
66-
zoneValues: {'_name': 'outer'}
67-
);
68-
}, onError: _onErrorWithLongStackTrace);
69-
} else {
70-
_outerZone = Zone.current.fork(
71-
specification: new ZoneSpecification(
72-
scheduleMicrotask: _scheduleMicrotask,
73-
run: _outerRun,
74-
runUnary: _outerRunUnary,
75-
runBinary: _outerRunBinary,
58+
() => _createOuterZone(Zone.current),
59+
onError: _onErrorWithLongStackTrace);
60+
} else {
61+
_outerZone = _createOuterZone(
62+
Zone.current,
7663
handleUncaughtError: (Zone self, ZoneDelegate parent, Zone zone, error, StackTrace trace) =>
7764
_onErrorWithoutLongStackTrace(error, trace)
78-
),
79-
zoneValues: {'_name': 'outer'}
8065
);
8166
}
8267

@@ -240,4 +225,17 @@ class VmTurnZone {
240225
throw error;
241226
}
242227
}
228+
229+
Zone _createOuterZone(Zone zone, {handleUncaughtError}) {
230+
return zone.fork(
231+
specification: new ZoneSpecification(
232+
scheduleMicrotask: _scheduleMicrotask,
233+
run: _outerRun,
234+
runUnary: _outerRunUnary,
235+
runBinary: _outerRunBinary,
236+
handleUncaughtError: handleUncaughtError
237+
),
238+
zoneValues: {'_name': 'outer'}
239+
);
240+
}
243241
}

modules/angular2/src/core/zone/vm_turn_zone.es6

Lines changed: 78 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,28 @@ import {normalizeBlank, isPresent, global} from 'angular2/src/facade/lang';
1313
* @exportedAs angular2/core
1414
*/
1515
export 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
}

modules/angular2/src/test_lib/test_lib.es6

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {DOM} from 'angular2/src/dom/dom_adapter';
22
import {StringMapWrapper} from 'angular2/src/facade/collection';
3+
import {global} from 'angular2/src/facade/lang';
34

45
import {bind} from 'angular2/di';
56

@@ -136,14 +137,11 @@ function _it(jsmFn, name, fn) {
136137
fn = inject([], fn);
137138
}
138139

139-
// Use setTimeout() to run tests in a macrotasks.
140-
// Without this, tests would be executed in the context of a microtasks (a promise .then).
141-
setTimeout(() => {
142-
inIt = true;
143-
fn.execute(injector);
144-
inIt = false;
145-
if (!async) done();
146-
}, 0);
140+
inIt = true;
141+
fn.execute(injector);
142+
inIt = false;
143+
144+
if (!async) done();
147145
});
148146
}
149147

@@ -357,5 +355,5 @@ function elementText(n) {
357355
}
358356

359357
function getCurrentZoneName(): string {
360-
return zone._name;
358+
return global.zone._name;
361359
}

tools/broccoli/html-replace/SCRIPTS.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
<script src="es6-promise.js" type="text/javascript"></script>
2-
<script src="zone.js" type="text/javascript"></script>
1+
<script src="zone-microtask.js" type="text/javascript"></script>
32
<script src="long-stack-trace-zone.js" type="text/javascript"></script>
43
<script src="traceur-runtime.js" type="text/javascript"></script>
54
<script src="es6-module-loader-sans-promises.src.js" type="text/javascript"></script>

tools/broccoli/html-replace/SCRIPTS_benchmarks.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
<script src="es6-promise.js" type="text/javascript"></script>
2-
<script src="zone.js" type="text/javascript"></script>
1+
<script src="zone-microtask.js" type="text/javascript"></script>
32
<script src="long-stack-trace-zone.js" type="text/javascript"></script>
43
<script src="url_params_to_form.js" type="text/javascript"></script>
54
<script src="traceur-runtime.js" type="text/javascript"></script>

tools/broccoli/html-replace/SCRIPTS_benchmarks_external.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
<script src="es6-promise.js" type="text/javascript"></script>
2-
<script src="zone.js" type="text/javascript"></script>
1+
<script src="zone-microtask.js" type="text/javascript"></script>
32
<script src="long-stack-trace-zone.js" type="text/javascript"></script>
43
<script src="angular.js" type="text/javascript"></script>
54
<script src="url_params_to_form.js" type="text/javascript"></script>

tools/broccoli/trees/browser_tree.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ module.exports = function makeBrowserTree(options, destinationPath) {
7878

7979
var vendorScriptsTree = flatten(new Funnel('.', {
8080
files: [
81-
'zone/es6-promise.js',
82-
'zone/zone.js',
81+
'zone/zone-microtask.js',
8382
'zone/long-stack-trace-zone.js',
8483
'node_modules/es6-module-loader/dist/es6-module-loader-sans-promises.src.js',
8584
'node_modules/systemjs/dist/system.src.js',

0 commit comments

Comments
 (0)
X Tutup