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.
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
'node_modules/systemjs/lib/extension-register.js',
|
||||
'node_modules/zone.js/zone.js',
|
||||
|
||||
'tools/build/file2modulename.js',
|
||||
'test-main.js'
|
||||
|
|
|
@ -83,6 +83,7 @@ export function bootstrap(appComponentType: Type, bindings=null) {
|
|||
var appInjector = _rootInjector.createChild(_injectorBindings(
|
||||
appComponentType));
|
||||
if (isPresent(bindings)) appInjector = appInjector.createChild(bindings);
|
||||
|
||||
return appInjector.asyncGet(ChangeDetector).then((cd) => {
|
||||
// TODO(rado): replace with zone.
|
||||
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);
|
||||
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 {
|
||||
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 removeAt(List l, int index) { l.removeAt(index); }
|
||||
static void clear(List l) { l.clear(); }
|
||||
static String join(List l, String s) => l.join(s);
|
||||
}
|
||||
|
||||
bool isListLikeIterable(obj) => obj is Iterable;
|
||||
|
|
|
@ -121,11 +121,16 @@ export class ListWrapper {
|
|||
list.splice(index, 0, value);
|
||||
}
|
||||
static removeAt(list, index:int) {
|
||||
var res = list[index];
|
||||
list.splice(index, 1);
|
||||
return res;
|
||||
}
|
||||
static clear(list) {
|
||||
list.splice(0, list.length);
|
||||
}
|
||||
static join(list, s) {
|
||||
return list.join(s);
|
||||
}
|
||||
}
|
||||
|
||||
export function isListLikeIterable(obj):boolean {
|
||||
|
|
|
@ -66,4 +66,4 @@ _handleAsync(fn) {
|
|||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
}
|
|
@ -77,7 +77,6 @@ window.beforeEach(function() {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
function mapToString(m) {
|
||||
if (!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 {MapWrapper} from 'facade/collection';
|
||||
import {describe, it, iit, ddescribe, expect, tick, async} from 'test_lib/test_lib';
|
||||
import {MapWrapper, ListWrapper} from 'facade/collection';
|
||||
import {PromiseWrapper} from 'facade/async';
|
||||
|
||||
class TestObj {
|
||||
prop;
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
"systemjs": "^0.9.1",
|
||||
"through2": "^0.6.1",
|
||||
"traceur": "0.0.74",
|
||||
"which": "~1"
|
||||
"which": "~1",
|
||||
"zone.js": "0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bower": "^1.3.12",
|
||||
|
|
Loading…
Reference in New Issue