Skip to content

Commit 21f60c5

Browse files
committed
refactor(WebWorker): Abstract message passing and serialization to UIMessageBroker
closes angular#3703 Closes angular#3815
1 parent aeef19e commit 21f60c5

22 files changed

+320
-249
lines changed

modules/angular2/src/web_workers/worker/broker.ts renamed to modules/angular2/src/web_workers/shared/client_message_broker.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ import {Injectable} from "angular2/di";
1414
import {Type} from "angular2/src/facade/lang";
1515

1616
@Injectable()
17-
export class MessageBrokerFactory {
17+
export class ClientMessageBrokerFactory {
1818
constructor(private _messageBus: MessageBus, protected _serializer: Serializer) {}
1919

20-
createMessageBroker(channel: string): MessageBroker {
21-
return new MessageBroker(this._messageBus, this._serializer, channel);
20+
createMessageBroker(channel: string): ClientMessageBroker {
21+
return new ClientMessageBroker(this._messageBus, this._serializer, channel);
2222
}
2323
}
2424

25-
export class MessageBroker {
25+
export class ClientMessageBroker {
2626
private _pending: Map<string, PromiseCompleter<any>> = new Map<string, PromiseCompleter<any>>();
2727
private _sink: EventEmitter;
2828

modules/angular2/src/web_workers/shared/serializer.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ import {
4040
RenderViewWithFragmentsStore
4141
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
4242

43+
// PRIMITIVE is any type that does not need to be serialized (string, number, boolean)
44+
// We set it to String so that it is considered a Type.
45+
export const PRIMITIVE: Type = String;
46+
4347
@Injectable()
4448
export class Serializer {
4549
private _enumRegistry: Map<any, Map<number, any>>;
@@ -76,7 +80,7 @@ export class Serializer {
7680
ListWrapper.forEach(obj, (val) => { serializedObj.push(this.serialize(val, type)); });
7781
return serializedObj;
7882
}
79-
if (type == String) {
83+
if (type == PRIMITIVE) {
8084
return obj;
8185
}
8286
if (type == ViewDefinition) {
@@ -119,7 +123,7 @@ export class Serializer {
119123
ListWrapper.forEach(map, (val) => { obj.push(this.deserialize(val, type, data)); });
120124
return obj;
121125
}
122-
if (type == String) {
126+
if (type == PRIMITIVE) {
123127
return map;
124128
}
125129

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import {Injectable} from 'angular2/di';
2+
import {ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
3+
import {Serializer} from "angular2/src/web_workers/shared/serializer";
4+
import {isPresent, Type, FunctionWrapper} from "angular2/src/facade/lang";
5+
import {MessageBus} from "angular2/src/web_workers/shared/message_bus";
6+
import {EventEmitter, Promise, PromiseWrapper, ObservableWrapper} from 'angular2/src/facade/async';
7+
8+
@Injectable()
9+
export class ServiceMessageBrokerFactory {
10+
constructor(private _messageBus: MessageBus, protected _serializer: Serializer) {}
11+
12+
createMessageBroker(channel: string): ServiceMessageBroker {
13+
return new ServiceMessageBroker(this._messageBus, this._serializer, channel);
14+
}
15+
}
16+
17+
/**
18+
* Helper class for UIComponents that allows components to register methods.
19+
* If a registered method message is received from the broker on the worker,
20+
* the UIMessageBroker desererializes its arguments and calls the registered method.
21+
* If that method returns a promise, the UIMessageBroker returns the result to the worker.
22+
*/
23+
export class ServiceMessageBroker {
24+
private _sink: EventEmitter;
25+
private _methods: Map<string, Function> = new Map<string, Function>();
26+
27+
constructor(messageBus: MessageBus, private _serializer: Serializer, public channel) {
28+
this._sink = messageBus.to(channel);
29+
var source = messageBus.from(channel);
30+
ObservableWrapper.subscribe(source, (message) => this._handleMessage(message));
31+
}
32+
33+
registerMethod(methodName: string, signature: List<Type>, method: Function, returnType?: Type):
34+
void {
35+
this._methods.set(methodName, (message: ReceivedMessage) => {
36+
var serializedArgs = message.args;
37+
var deserializedArgs: List<any> = ListWrapper.createFixedSize(signature.length);
38+
for (var i = 0; i < signature.length; i++) {
39+
var serializedArg = serializedArgs[i];
40+
deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature[i]);
41+
}
42+
43+
var promise = FunctionWrapper.apply(method, deserializedArgs);
44+
if (isPresent(returnType) && isPresent(promise)) {
45+
this._wrapWebWorkerPromise(message.id, promise, returnType);
46+
}
47+
});
48+
}
49+
50+
private _handleMessage(map: StringMap<string, any>): void {
51+
var message = new ReceivedMessage(map);
52+
if (this._methods.has(message.method)) {
53+
this._methods.get(message.method)(message);
54+
}
55+
}
56+
57+
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
58+
PromiseWrapper.then(promise, (result: any) => {
59+
ObservableWrapper.callNext(
60+
this._sink,
61+
{'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id});
62+
});
63+
}
64+
}
65+
66+
export class ReceivedMessage {
67+
method: string;
68+
args: List<any>;
69+
id: string;
70+
type: string;
71+
72+
constructor(data: StringMap<string, any>) {
73+
this.method = data['method'];
74+
this.args = data['args'];
75+
this.id = data['id'];
76+
this.type = data['type'];
77+
}
78+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
library angular2.src.web_workers.ui.bind;
2+
3+
/**
4+
* Binding is not necessary in dart.
5+
* This method just returns the passed function regardless of scope.
6+
* It's only here to match the TypeScript implementation.
7+
* TODO(jteplitz602) Have ts2dart remove calls to bind(#3820)
8+
*/
9+
Function bind(Function fn, dynamic scope) {
10+
return fn;
11+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function bind(fn: Function, scope: any): Function {
2+
return fn.bind(scope);
3+
}

modules/angular2/src/web_workers/ui/di_bindings.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import {MessageBasedRenderCompiler} from 'angular2/src/web_workers/ui/render_com
6565
import {MessageBasedRenderer} from 'angular2/src/web_workers/ui/renderer';
6666
import {MessageBasedXHRImpl} from 'angular2/src/web_workers/ui/xhr_impl';
6767
import {WebWorkerSetup} from 'angular2/src/web_workers/ui/setup';
68+
import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker';
6869

6970
var _rootInjector: Injector;
7071

@@ -135,7 +136,8 @@ function _injectorBindings(): List<any> {
135136
WebWorkerSetup,
136137
MessageBasedRenderCompiler,
137138
MessageBasedXHRImpl,
138-
MessageBasedRenderer
139+
MessageBasedRenderer,
140+
ServiceMessageBrokerFactory
139141
];
140142
}
141143

modules/angular2/src/web_workers/ui/impl.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,3 @@ export class WebWorkerMain {
3737
public renderer: MessageBasedRenderer, public xhr: MessageBasedXHRImpl,
3838
public setup: WebWorkerSetup) {}
3939
}
40-
41-
export class ReceivedMessage {
42-
method: string;
43-
args: List<any>;
44-
id: string;
45-
type: string;
46-
47-
constructor(data: StringMap<string, any>) {
48-
this.method = data['method'];
49-
this.args = data['args'];
50-
this.id = data['id'];
51-
this.type = data['type'];
52-
}
53-
}
Lines changed: 13 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import {Injectable} from 'angular2/di';
2-
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
3-
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
42
import {
53
RenderDirectiveMetadata,
64
ProtoViewDto,
@@ -9,55 +7,22 @@ import {
97
RenderProtoViewMergeMapping,
108
RenderCompiler
119
} from 'angular2/src/render/api';
12-
import {EventEmitter, ObservableWrapper, PromiseWrapper, Promise} from 'angular2/src/facade/async';
1310
import {RENDER_COMPILER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
14-
import {ReceivedMessage} from 'angular2/src/web_workers/ui/impl';
15-
import {BaseException, Type} from 'angular2/src/facade/lang';
11+
import {bind} from './bind';
12+
import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker';
1613

17-
// TODO(jteplitz602): Create parent UIComponent class #3703
1814
@Injectable()
1915
export class MessageBasedRenderCompiler {
20-
private _sink: EventEmitter;
21-
private _source: EventEmitter;
22-
23-
constructor(bus: MessageBus, private _serializer: Serializer,
24-
private _renderCompiler: RenderCompiler) {
25-
this._sink = bus.to(RENDER_COMPILER_CHANNEL);
26-
this._source = bus.from(RENDER_COMPILER_CHANNEL);
27-
ObservableWrapper.subscribe(this._source,
28-
(message: StringMap<string, any>) => this._handleMessage(message));
29-
}
30-
31-
private _handleMessage(map: StringMap<string, any>): void {
32-
var message = new ReceivedMessage(map);
33-
var args = message.args;
34-
var promise: Promise<any>;
35-
switch (message.method) {
36-
case "compileHost":
37-
var directiveMetadata = this._serializer.deserialize(args[0], RenderDirectiveMetadata);
38-
promise = this._renderCompiler.compileHost(directiveMetadata);
39-
this._wrapWebWorkerPromise(message.id, promise, ProtoViewDto);
40-
break;
41-
case "compile":
42-
var view = this._serializer.deserialize(args[0], ViewDefinition);
43-
promise = this._renderCompiler.compile(view);
44-
this._wrapWebWorkerPromise(message.id, promise, ProtoViewDto);
45-
break;
46-
case "mergeProtoViewsRecursively":
47-
var views = this._serializer.deserialize(args[0], RenderProtoViewRef);
48-
promise = this._renderCompiler.mergeProtoViewsRecursively(views);
49-
this._wrapWebWorkerPromise(message.id, promise, RenderProtoViewMergeMapping);
50-
break;
51-
default:
52-
throw new BaseException("not implemented");
53-
}
54-
}
55-
56-
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
57-
PromiseWrapper.then(promise, (result: any) => {
58-
ObservableWrapper.callNext(
59-
this._sink,
60-
{'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id});
61-
});
16+
constructor(brokerFactory: ServiceMessageBrokerFactory, private _renderCompiler: RenderCompiler) {
17+
var broker = brokerFactory.createMessageBroker(RENDER_COMPILER_CHANNEL);
18+
broker.registerMethod("compileHost", [RenderDirectiveMetadata],
19+
bind(this._renderCompiler.compileHost, this._renderCompiler),
20+
ProtoViewDto);
21+
broker.registerMethod("compile", [ViewDefinition],
22+
bind(this._renderCompiler.compile, this._renderCompiler), ProtoViewDto);
23+
broker.registerMethod(
24+
"mergeProtoViewsRecursively", [RenderProtoViewRef],
25+
bind(this._renderCompiler.mergeProtoViewsRecursively, this._renderCompiler),
26+
RenderProtoViewMergeMapping);
6227
}
6328
}

0 commit comments

Comments
 (0)