Skip to content

Commit df36ffb

Browse files
committed
feat(zone): add initial implementation of VmTurnZone
1 parent 4a08bbf commit df36ffb

File tree

14 files changed

+269
-5
lines changed

14 files changed

+269
-5
lines changed

karma-js.conf.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module.exports = function(config) {
1919
// Including systemjs because it defines `__eval`, which produces correct stack traces.
2020
'node_modules/systemjs/dist/system.src.js',
2121
'node_modules/systemjs/lib/extension-register.js',
22+
'node_modules/zone.js/zone.js',
2223

2324
'tools/build/file2modulename.js',
2425
'test-main.js'

modules/core/src/application.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export function bootstrap(appComponentType: Type, bindings=null) {
8383
var appInjector = _rootInjector.createChild(_injectorBindings(
8484
appComponentType));
8585
if (isPresent(bindings)) appInjector = appInjector.createChild(bindings);
86+
8687
return appInjector.asyncGet(ChangeDetector).then((cd) => {
8788
// TODO(rado): replace with zone.
8889
cd.detectChanges();
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
library angular.zone;
2+
3+
import 'dart:async' as async;
4+
5+
class VmTurnZone {
6+
Function _onTurnStart;
7+
Function _onTurnDone;
8+
Function _onScheduleMicrotask;
9+
10+
async.Zone _outerZone;
11+
async.Zone _innerZone;
12+
13+
int _nestedRunCounter;
14+
15+
VmTurnZone() {
16+
_nestedRunCounter = 0;
17+
_outerZone = async.Zone.current;
18+
_innerZone = _outerZone.fork(specification: new async.ZoneSpecification(
19+
run: _onRun,
20+
runUnary: _onRunUnary,
21+
scheduleMicrotask: _onMicrotask
22+
));
23+
}
24+
25+
initCallbacks({Function onTurnStart, Function onTurnDone, Function onScheduleMicrotask}) {
26+
this._onTurnStart = onTurnStart;
27+
this._onTurnDone = onTurnDone;
28+
this._onScheduleMicrotask = onScheduleMicrotask;
29+
}
30+
31+
dynamic run(fn()) => _innerZone.run(fn);
32+
33+
dynamic runOutsideAngular(fn()) => _outerZone.run(fn);
34+
35+
36+
dynamic _onRunBase(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) {
37+
_nestedRunCounter++;
38+
try {
39+
if (_nestedRunCounter == 1 && _onTurnStart != null) delegate.run(zone, _onTurnStart);
40+
41+
return fn();
42+
43+
} finally {
44+
_nestedRunCounter--;
45+
if (_nestedRunCounter == 0 && _onTurnDone != null) _finishTurn(zone, delegate);
46+
}
47+
}
48+
49+
dynamic _onRun(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) =>
50+
_onRunBase(self, delegate, zone, () => delegate.run(zone, fn));
51+
52+
dynamic _onRunUnary(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn(args), args) =>
53+
_onRunBase(self, delegate, zone, () => delegate.runUnary(zone, fn, args));
54+
55+
void _finishTurn(zone, delegate) {
56+
delegate.run(zone, _onTurnDone);
57+
}
58+
59+
_onMicrotask(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn) {
60+
if (this._onScheduleMicrotask != null) {
61+
this._onScheduleMicrotask(fn);
62+
} else {
63+
delegate.scheduleMicrotask(zone, fn);
64+
}
65+
}
66+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {List, ListWrapper} from 'facade/collection';
2+
import {normalizeBlank} from 'facade/lang';
3+
4+
export class VmTurnZone {
5+
_outerZone;
6+
_innerZone;
7+
8+
_onTurnStart:Function;
9+
_onTurnDone:Function;
10+
11+
_nestedRunCounter:number;
12+
13+
constructor() {
14+
this._nestedRunCounter = 0;
15+
this._onTurnStart = null;
16+
this._onTurnDone = null;
17+
18+
this._outerZone = window.zone;
19+
this._innerZone = this._outerZone.fork({
20+
beforeTask: () => this._beforeTask(),
21+
afterTask: () => this._afterTask()
22+
});
23+
}
24+
25+
initCallbacks({onTurnStart, onTurnDone, onScheduleMicrotask} = {}) {
26+
this._onTurnStart = normalizeBlank(onTurnStart);
27+
this._onTurnDone = normalizeBlank(onTurnDone);
28+
}
29+
30+
run(fn) {
31+
return this._innerZone.run(fn);
32+
}
33+
34+
runOutsideAngular(fn) {
35+
return this._outerZone.run(fn);
36+
}
37+
38+
39+
_beforeTask(){
40+
this._nestedRunCounter ++;
41+
if(this._nestedRunCounter === 1 && this._onTurnStart) {
42+
this._onTurnStart();
43+
}
44+
}
45+
46+
_afterTask(){
47+
this._nestedRunCounter --;
48+
if(this._nestedRunCounter === 0 && this._onTurnDone) {
49+
this._onTurnDone();
50+
}
51+
}
52+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, async, tick} from 'test_lib/test_lib';
2+
import {Log, once} from 'test_lib/utils';
3+
import {PromiseWrapper} from 'facade/async';
4+
import {BaseException} from 'facade/lang';
5+
import {VmTurnZone} from 'core/zone/vm_turn_zone';
6+
7+
export function main() {
8+
describe("VmTurnZone", () => {
9+
var log, zone;
10+
11+
beforeEach(() => {
12+
log = new Log();
13+
zone = new VmTurnZone();
14+
zone.initCallbacks({
15+
onTurnStart: log.fn('onTurnStart'),
16+
onTurnDone: log.fn('onTurnDone')
17+
});
18+
});
19+
20+
describe("run", () => {
21+
it('should call onTurnStart and onTurnDone', () => {
22+
zone.run(log.fn('run'));
23+
24+
expect(log.result()).toEqual('onTurnStart; run; onTurnDone');
25+
});
26+
27+
it('should return the body return value from run', () => {
28+
expect(zone.run(() => 6)).toEqual(6);
29+
});
30+
31+
it('should not run onTurnStart and onTurnDone for nested Zone.run', () => {
32+
zone.run(() => {
33+
zone.run(log.fn('run'));
34+
});
35+
expect(log.result()).toEqual('onTurnStart; run; onTurnDone');
36+
});
37+
38+
39+
it('should call onTurnStart and onTurnDone before and after each top-level run', () => {
40+
zone.run(log.fn('run1'));
41+
zone.run(log.fn('run2'));
42+
43+
expect(log.result()).toEqual('onTurnStart; run1; onTurnDone; onTurnStart; run2; onTurnDone');
44+
});
45+
46+
47+
it('should call onTurnStart and onTurnDone before and after each turn', (done) => {
48+
var a = PromiseWrapper.completer();
49+
var b = PromiseWrapper.completer();
50+
51+
zone.run(() => {
52+
log.add('run start');
53+
a.promise.then((_) => log.add('a then'));
54+
b.promise.then((_) => log.add('b then'));
55+
});
56+
57+
a.complete("a");
58+
b.complete("b");
59+
60+
PromiseWrapper.all([a.promise, b.promise]).then((_) => {
61+
expect(log.result()).toEqual('onTurnStart; run start; onTurnDone; onTurnStart; a then; onTurnDone; onTurnStart; b then; onTurnDone');
62+
done();
63+
});
64+
});
65+
});
66+
67+
describe("runOutsideAngular", () => {
68+
it("should run a function outside of the angular zone", () => {
69+
zone.runOutsideAngular(log.fn('run'));
70+
71+
expect(log.result()).toEqual('run');
72+
});
73+
});
74+
75+
describe("exceptions", () => {
76+
it('should rethrow exceptions from the body', () => {
77+
expect(() => {
78+
zone.run(() => {
79+
throw new BaseException('hello');
80+
});
81+
}).toThrowError('hello');
82+
});
83+
});
84+
});
85+
}

modules/facade/src/async.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,17 @@ class PromiseWrapper {
2020
if (success == null) return promise.catchError(onError);
2121
return promise.then(success, onError: onError);
2222
}
23+
24+
static completer(){
25+
return new _Completer(new Completer());
26+
}
27+
}
28+
29+
class _Completer {
30+
Completer c;
31+
_Completer(this.c);
32+
33+
get promise => c.future;
34+
get complete => c.complete;
35+
2336
}

modules/facade/src/async.es6

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,20 @@ export class PromiseWrapper {
1717
static then(promise:Promise, success:Function, rejection:Function):Promise {
1818
return promise.then(success, rejection);
1919
}
20+
21+
static completer() {
22+
var resolve;
23+
var reject;
24+
25+
var p = new Promise(function(res, rej) {
26+
resolve = res;
27+
reject = rej;
28+
});
29+
30+
return {
31+
promise: p,
32+
complete: resolve,
33+
reject: reject
34+
};
35+
}
2036
}

modules/facade/src/collection.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class ListWrapper {
9797
static void insert(List l, int index, value) { l.insert(index, value); }
9898
static void removeAt(List l, int index) { l.removeAt(index); }
9999
static void clear(List l) { l.clear(); }
100+
static String join(List l, String s) => l.join(s);
100101
}
101102

102103
bool isListLikeIterable(obj) => obj is Iterable;

modules/facade/src/collection.es6

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,16 @@ export class ListWrapper {
121121
list.splice(index, 0, value);
122122
}
123123
static removeAt(list, index:int) {
124+
var res = list[index];
124125
list.splice(index, 1);
126+
return res;
125127
}
126128
static clear(list) {
127129
list.splice(0, list.length);
128130
}
131+
static join(list, s) {
132+
return list.join(s);
133+
}
129134
}
130135

131136
export function isListLikeIterable(obj):boolean {

modules/test_lib/src/test_lib.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,4 @@ _handleAsync(fn) {
6666
}
6767

6868
return fn;
69-
}
69+
}

0 commit comments

Comments
 (0)