feat(zone): add initial implementation of VmTurnZone
This commit is contained in:
parent
4a08bbf7f1
commit
df36ffb11d
|
@ -19,6 +19,7 @@ module.exports = function(config) {
|
||||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||||
'node_modules/systemjs/dist/system.src.js',
|
'node_modules/systemjs/dist/system.src.js',
|
||||||
'node_modules/systemjs/lib/extension-register.js',
|
'node_modules/systemjs/lib/extension-register.js',
|
||||||
|
'node_modules/zone.js/zone.js',
|
||||||
|
|
||||||
'tools/build/file2modulename.js',
|
'tools/build/file2modulename.js',
|
||||||
'test-main.js'
|
'test-main.js'
|
||||||
|
|
|
@ -83,6 +83,7 @@ export function bootstrap(appComponentType: Type, bindings=null) {
|
||||||
var appInjector = _rootInjector.createChild(_injectorBindings(
|
var appInjector = _rootInjector.createChild(_injectorBindings(
|
||||||
appComponentType));
|
appComponentType));
|
||||||
if (isPresent(bindings)) appInjector = appInjector.createChild(bindings);
|
if (isPresent(bindings)) appInjector = appInjector.createChild(bindings);
|
||||||
|
|
||||||
return appInjector.asyncGet(ChangeDetector).then((cd) => {
|
return appInjector.asyncGet(ChangeDetector).then((cd) => {
|
||||||
// TODO(rado): replace with zone.
|
// TODO(rado): replace with zone.
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
library angular.zone;
|
||||||
|
|
||||||
|
import 'dart:async' as async;
|
||||||
|
|
||||||
|
class VmTurnZone {
|
||||||
|
Function _onTurnStart;
|
||||||
|
Function _onTurnDone;
|
||||||
|
Function _onScheduleMicrotask;
|
||||||
|
|
||||||
|
async.Zone _outerZone;
|
||||||
|
async.Zone _innerZone;
|
||||||
|
|
||||||
|
int _nestedRunCounter;
|
||||||
|
|
||||||
|
VmTurnZone() {
|
||||||
|
_nestedRunCounter = 0;
|
||||||
|
_outerZone = async.Zone.current;
|
||||||
|
_innerZone = _outerZone.fork(specification: new async.ZoneSpecification(
|
||||||
|
run: _onRun,
|
||||||
|
runUnary: _onRunUnary,
|
||||||
|
scheduleMicrotask: _onMicrotask
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
initCallbacks({Function onTurnStart, Function onTurnDone, Function onScheduleMicrotask}) {
|
||||||
|
this._onTurnStart = onTurnStart;
|
||||||
|
this._onTurnDone = onTurnDone;
|
||||||
|
this._onScheduleMicrotask = onScheduleMicrotask;
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic run(fn()) => _innerZone.run(fn);
|
||||||
|
|
||||||
|
dynamic runOutsideAngular(fn()) => _outerZone.run(fn);
|
||||||
|
|
||||||
|
|
||||||
|
dynamic _onRunBase(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) {
|
||||||
|
_nestedRunCounter++;
|
||||||
|
try {
|
||||||
|
if (_nestedRunCounter == 1 && _onTurnStart != null) delegate.run(zone, _onTurnStart);
|
||||||
|
|
||||||
|
return fn();
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
_nestedRunCounter--;
|
||||||
|
if (_nestedRunCounter == 0 && _onTurnDone != null) _finishTurn(zone, delegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic _onRun(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) =>
|
||||||
|
_onRunBase(self, delegate, zone, () => delegate.run(zone, fn));
|
||||||
|
|
||||||
|
dynamic _onRunUnary(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn(args), args) =>
|
||||||
|
_onRunBase(self, delegate, zone, () => delegate.runUnary(zone, fn, args));
|
||||||
|
|
||||||
|
void _finishTurn(zone, delegate) {
|
||||||
|
delegate.run(zone, _onTurnDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onMicrotask(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn) {
|
||||||
|
if (this._onScheduleMicrotask != null) {
|
||||||
|
this._onScheduleMicrotask(fn);
|
||||||
|
} else {
|
||||||
|
delegate.scheduleMicrotask(zone, fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
import {List, ListWrapper} from 'facade/collection';
|
||||||
|
import {normalizeBlank} from 'facade/lang';
|
||||||
|
|
||||||
|
export class VmTurnZone {
|
||||||
|
_outerZone;
|
||||||
|
_innerZone;
|
||||||
|
|
||||||
|
_onTurnStart:Function;
|
||||||
|
_onTurnDone:Function;
|
||||||
|
|
||||||
|
_nestedRunCounter:number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._nestedRunCounter = 0;
|
||||||
|
this._onTurnStart = null;
|
||||||
|
this._onTurnDone = null;
|
||||||
|
|
||||||
|
this._outerZone = window.zone;
|
||||||
|
this._innerZone = this._outerZone.fork({
|
||||||
|
beforeTask: () => this._beforeTask(),
|
||||||
|
afterTask: () => this._afterTask()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initCallbacks({onTurnStart, onTurnDone, onScheduleMicrotask} = {}) {
|
||||||
|
this._onTurnStart = normalizeBlank(onTurnStart);
|
||||||
|
this._onTurnDone = normalizeBlank(onTurnDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
run(fn) {
|
||||||
|
return this._innerZone.run(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
runOutsideAngular(fn) {
|
||||||
|
return this._outerZone.run(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_beforeTask(){
|
||||||
|
this._nestedRunCounter ++;
|
||||||
|
if(this._nestedRunCounter === 1 && this._onTurnStart) {
|
||||||
|
this._onTurnStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_afterTask(){
|
||||||
|
this._nestedRunCounter --;
|
||||||
|
if(this._nestedRunCounter === 0 && this._onTurnDone) {
|
||||||
|
this._onTurnDone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, async, tick} from 'test_lib/test_lib';
|
||||||
|
import {Log, once} from 'test_lib/utils';
|
||||||
|
import {PromiseWrapper} from 'facade/async';
|
||||||
|
import {BaseException} from 'facade/lang';
|
||||||
|
import {VmTurnZone} from 'core/zone/vm_turn_zone';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe("VmTurnZone", () => {
|
||||||
|
var log, zone;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
log = new Log();
|
||||||
|
zone = new VmTurnZone();
|
||||||
|
zone.initCallbacks({
|
||||||
|
onTurnStart: log.fn('onTurnStart'),
|
||||||
|
onTurnDone: log.fn('onTurnDone')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("run", () => {
|
||||||
|
it('should call onTurnStart and onTurnDone', () => {
|
||||||
|
zone.run(log.fn('run'));
|
||||||
|
|
||||||
|
expect(log.result()).toEqual('onTurnStart; run; onTurnDone');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the body return value from run', () => {
|
||||||
|
expect(zone.run(() => 6)).toEqual(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not run onTurnStart and onTurnDone for nested Zone.run', () => {
|
||||||
|
zone.run(() => {
|
||||||
|
zone.run(log.fn('run'));
|
||||||
|
});
|
||||||
|
expect(log.result()).toEqual('onTurnStart; run; onTurnDone');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should call onTurnStart and onTurnDone before and after each top-level run', () => {
|
||||||
|
zone.run(log.fn('run1'));
|
||||||
|
zone.run(log.fn('run2'));
|
||||||
|
|
||||||
|
expect(log.result()).toEqual('onTurnStart; run1; onTurnDone; onTurnStart; run2; onTurnDone');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should call onTurnStart and onTurnDone before and after each turn', (done) => {
|
||||||
|
var a = PromiseWrapper.completer();
|
||||||
|
var b = PromiseWrapper.completer();
|
||||||
|
|
||||||
|
zone.run(() => {
|
||||||
|
log.add('run start');
|
||||||
|
a.promise.then((_) => log.add('a then'));
|
||||||
|
b.promise.then((_) => log.add('b then'));
|
||||||
|
});
|
||||||
|
|
||||||
|
a.complete("a");
|
||||||
|
b.complete("b");
|
||||||
|
|
||||||
|
PromiseWrapper.all([a.promise, b.promise]).then((_) => {
|
||||||
|
expect(log.result()).toEqual('onTurnStart; run start; onTurnDone; onTurnStart; a then; onTurnDone; onTurnStart; b then; onTurnDone');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("runOutsideAngular", () => {
|
||||||
|
it("should run a function outside of the angular zone", () => {
|
||||||
|
zone.runOutsideAngular(log.fn('run'));
|
||||||
|
|
||||||
|
expect(log.result()).toEqual('run');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("exceptions", () => {
|
||||||
|
it('should rethrow exceptions from the body', () => {
|
||||||
|
expect(() => {
|
||||||
|
zone.run(() => {
|
||||||
|
throw new BaseException('hello');
|
||||||
|
});
|
||||||
|
}).toThrowError('hello');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -20,4 +20,17 @@ class PromiseWrapper {
|
||||||
if (success == null) return promise.catchError(onError);
|
if (success == null) return promise.catchError(onError);
|
||||||
return promise.then(success, onError: onError);
|
return promise.then(success, onError: onError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static completer(){
|
||||||
|
return new _Completer(new Completer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Completer {
|
||||||
|
Completer c;
|
||||||
|
_Completer(this.c);
|
||||||
|
|
||||||
|
get promise => c.future;
|
||||||
|
get complete => c.complete;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,4 +17,20 @@ export class PromiseWrapper {
|
||||||
static then(promise:Promise, success:Function, rejection:Function):Promise {
|
static then(promise:Promise, success:Function, rejection:Function):Promise {
|
||||||
return promise.then(success, rejection);
|
return promise.then(success, rejection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static completer() {
|
||||||
|
var resolve;
|
||||||
|
var reject;
|
||||||
|
|
||||||
|
var p = new Promise(function(res, rej) {
|
||||||
|
resolve = res;
|
||||||
|
reject = rej;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
promise: p,
|
||||||
|
complete: resolve,
|
||||||
|
reject: reject
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -97,6 +97,7 @@ class ListWrapper {
|
||||||
static void insert(List l, int index, value) { l.insert(index, value); }
|
static void insert(List l, int index, value) { l.insert(index, value); }
|
||||||
static void removeAt(List l, int index) { l.removeAt(index); }
|
static void removeAt(List l, int index) { l.removeAt(index); }
|
||||||
static void clear(List l) { l.clear(); }
|
static void clear(List l) { l.clear(); }
|
||||||
|
static String join(List l, String s) => l.join(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isListLikeIterable(obj) => obj is Iterable;
|
bool isListLikeIterable(obj) => obj is Iterable;
|
||||||
|
|
|
@ -121,11 +121,16 @@ export class ListWrapper {
|
||||||
list.splice(index, 0, value);
|
list.splice(index, 0, value);
|
||||||
}
|
}
|
||||||
static removeAt(list, index:int) {
|
static removeAt(list, index:int) {
|
||||||
|
var res = list[index];
|
||||||
list.splice(index, 1);
|
list.splice(index, 1);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
static clear(list) {
|
static clear(list) {
|
||||||
list.splice(0, list.length);
|
list.splice(0, list.length);
|
||||||
}
|
}
|
||||||
|
static join(list, s) {
|
||||||
|
return list.join(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isListLikeIterable(obj):boolean {
|
export function isListLikeIterable(obj):boolean {
|
||||||
|
|
|
@ -77,7 +77,6 @@ window.beforeEach(function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function mapToString(m) {
|
function mapToString(m) {
|
||||||
if (!m) {
|
if (!m) {
|
||||||
return ''+m;
|
return ''+m;
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import {List, ListWrapper} from 'facade/collection';
|
||||||
|
|
||||||
|
export class Log {
|
||||||
|
_result:List;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._result = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
add(value) {
|
||||||
|
ListWrapper.push(this._result, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn(value) {
|
||||||
|
return () => {
|
||||||
|
ListWrapper.push(this._result, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result() {
|
||||||
|
return ListWrapper.join(this._result, "; ");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import {describe, it, iit, ddescribe, expect} from 'test_lib/test_lib';
|
import {describe, it, iit, ddescribe, expect, tick, async} from 'test_lib/test_lib';
|
||||||
import {MapWrapper} from 'facade/collection';
|
import {MapWrapper, ListWrapper} from 'facade/collection';
|
||||||
|
import {PromiseWrapper} from 'facade/async';
|
||||||
|
|
||||||
class TestObj {
|
class TestObj {
|
||||||
prop;
|
prop;
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
"systemjs": "^0.9.1",
|
"systemjs": "^0.9.1",
|
||||||
"through2": "^0.6.1",
|
"through2": "^0.6.1",
|
||||||
"traceur": "0.0.74",
|
"traceur": "0.0.74",
|
||||||
"which": "~1"
|
"which": "~1",
|
||||||
|
"zone.js": "0.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bower": "^1.3.12",
|
"bower": "^1.3.12",
|
||||||
|
|
Loading…
Reference in New Issue