fix(platform-webworker): integrate review feedback

closes #14581
This commit is contained in:
Victor Berchet 2017-02-20 09:33:11 -08:00
parent cdf99cf68b
commit 601fd3e305
7 changed files with 61 additions and 37 deletions

View File

@ -67,7 +67,7 @@ export class ClientMessageBroker_ extends ClientMessageBroker {
this._serializer = _serializer; this._serializer = _serializer;
const source = messageBus.from(channel); const source = messageBus.from(channel);
source.subscribe({next: (message: MessageData) => this._handleMessage(message)}); source.subscribe({next: (message: ResponseMessageData) => this._handleMessage(message)});
} }
private _generateMessageId(name: string): string { private _generateMessageId(name: string): string {
@ -102,31 +102,33 @@ export class ClientMessageBroker_ extends ClientMessageBroker {
this._pending.set(id, completer); this._pending.set(id, completer);
promise.catch((err) => { promise.catch((err) => {
if (console && console.log) { if (console && console.error) {
// tslint:disable-next-line:no-console // tslint:disable-next-line:no-console
console.log(err); console.error(err);
} }
completer.reject(err); completer.reject(err);
}); });
promise = promise.then( promise = promise.then(
(value: any) => (v: any) => this._serializer ? this._serializer.deserialize(v, returnType) : v);
this._serializer ? value : this._serializer.deserialize(value, returnType));
} else { } else {
promise = null; promise = null;
} }
const message = {'method': args.method, 'args': fnArgs}; const message: RequestMessageData = {
'method': args.method,
'args': fnArgs,
};
if (id != null) { if (id != null) {
(message as any)['id'] = id; message['id'] = id;
} }
this._sink.emit(message); this._sink.emit(message);
return promise; return promise;
} }
private _handleMessage(message: MessageData): void { private _handleMessage(message: ResponseMessageData): void {
if (message.type === 'result' || message.type === 'error') { if (message.type === 'result' || message.type === 'error') {
const id = message.id; const id = message.id;
if (this._pending.has(id)) { if (this._pending.has(id)) {
@ -141,7 +143,13 @@ export class ClientMessageBroker_ extends ClientMessageBroker {
} }
} }
interface MessageData { interface RequestMessageData {
method: string;
args?: any[];
id?: string;
}
interface ResponseMessageData {
type: 'result'|'error'; type: 'result'|'error';
value?: any; value?: any;
id?: string; id?: string;

View File

@ -17,18 +17,21 @@ export class RenderStore {
allocateId(): number { return this._nextIndex++; } allocateId(): number { return this._nextIndex++; }
store(obj: any, id: number): void { store(obj: any, id: number): void {
if (id == null) return;
this._lookupById.set(id, obj); this._lookupById.set(id, obj);
this._lookupByObject.set(obj, id); this._lookupByObject.set(obj, id);
} }
remove(obj: any): void { remove(obj: any): void {
const index = this._lookupByObject.get(obj); const index = this._lookupByObject.get(obj);
this._lookupByObject.delete(obj); if (index != null) {
this._lookupById.delete(index); this._lookupByObject.delete(obj);
this._lookupById.delete(index);
}
} }
deserialize(id: number): any { deserialize(id: number): any {
return id == null || !this._lookupById.has(id) ? null : this._lookupById.get(id); return this._lookupById.has(id) ? this._lookupById.get(id) : null;
} }
serialize(obj: any): number { return obj == null ? null : this._lookupByObject.get(obj); } serialize(obj: any): number { return obj == null ? null : this._lookupByObject.get(obj); }

View File

@ -19,7 +19,7 @@ import {RenderStore} from './render_store';
* @experimental WebWorker support in Angular is currently experimental. * @experimental WebWorker support in Angular is currently experimental.
* @deprecated in v4. Use SerializerTypes.PRIMITIVE instead * @deprecated in v4. Use SerializerTypes.PRIMITIVE instead
*/ */
export const PRIMITIVE: Type<any> = String; export const PRIMITIVE = SerializerTypes.PRIMITIVE;
export class LocationType { export class LocationType {
constructor( constructor(
@ -45,7 +45,7 @@ export class Serializer {
constructor(private _renderStore: RenderStore) {} constructor(private _renderStore: RenderStore) {}
serialize(obj: any, type: Type<any>|SerializerTypes = SerializerTypes.PRIMITIVE): Object { serialize(obj: any, type: Type<any>|SerializerTypes = SerializerTypes.PRIMITIVE): Object {
if (obj == null || type === PRIMITIVE || type === SerializerTypes.PRIMITIVE) { if (obj == null || type === SerializerTypes.PRIMITIVE) {
return obj; return obj;
} }
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
@ -68,7 +68,7 @@ export class Serializer {
deserialize(map: any, type: Type<any>|SerializerTypes = SerializerTypes.PRIMITIVE, data?: any): deserialize(map: any, type: Type<any>|SerializerTypes = SerializerTypes.PRIMITIVE, data?: any):
any { any {
if (map == null || type === PRIMITIVE || type === SerializerTypes.PRIMITIVE) { if (map == null || type === SerializerTypes.PRIMITIVE) {
return map; return map;
} }
if (Array.isArray(map)) { if (Array.isArray(map)) {

View File

@ -265,24 +265,21 @@ export class MessageBasedRendererV2 {
const methods: any[][] = [ const methods: any[][] = [
['createRenderer', this.createRenderer, RSO, CRT, P], ['createRenderer', this.createRenderer, RSO, CRT, P],
['createElement', this.createElement, RSO, P, P, P], ['createElement', this.createElement, RSO, P, P, P],
['createComment', this.createComment, RSO, P, P], ['createComment', this.createComment, RSO, P, P], ['createText', this.createText, RSO, P, P],
['createText', this.createText, RSO, P, P],
['appendChild', this.appendChild, RSO, RSO, RSO], ['appendChild', this.appendChild, RSO, RSO, RSO],
['insertBefore', this.insertBefore, RSO, RSO, RSO, RSO], ['insertBefore', this.insertBefore, RSO, RSO, RSO, RSO],
['removeChild', this.removeChild, RSO, RSO, RSO], ['removeChild', this.removeChild, RSO, RSO, RSO],
['selectRootElement', this.selectRootElement, RSO, P, P], ['selectRootElement', this.selectRootElement, RSO, P, P],
['parentNode', this.parentNode, RSO, RSO, P], ['parentNode', this.parentNode, RSO, RSO, P], ['nextSibling', this.nextSibling, RSO, RSO, P],
['nextSibling', this.nextSibling, RSO, RSO, P],
['setAttribute', this.setAttribute, RSO, RSO, P, P, P], ['setAttribute', this.setAttribute, RSO, RSO, P, P, P],
['removeAttribute', this.removeAttribute, RSO, RSO, P, P], ['removeAttribute', this.removeAttribute, RSO, RSO, P, P],
['addClass', this.addClass, RSO, RSO, P], ['addClass', this.addClass, RSO, RSO, P], ['removeClass', this.removeClass, RSO, RSO, P],
['removeClass', this.removeClass, RSO, RSO, P],
['setStyle', this.setStyle, RSO, RSO, P, P, P, P], ['setStyle', this.setStyle, RSO, RSO, P, P, P, P],
['removeStyle', this.removeStyle, RSO, RSO, P, P], ['removeStyle', this.removeStyle, RSO, RSO, P, P],
['setProperty', this.setProperty, RSO, RSO, P, P], ['setProperty', this.setProperty, RSO, RSO, P, P], ['setValue', this.setValue, RSO, RSO, P],
['setValue', this.setValue, RSO, RSO, P], ['listen', this.listen, RSO, RSO, P, P, P], ['unlisten', this.unlisten, RSO, RSO],
['listen', this.listen, RSO, RSO, P, P, P], ['destroy', this.destroy, RSO], ['destroyNode', this.destroyNode, RSO, P]
['unlisten', this.unlisten, RSO, RSO],
]; ];
methods.forEach(([name, method, ...argTypes]: any[]) => { methods.forEach(([name, method, ...argTypes]: any[]) => {
@ -290,6 +287,15 @@ export class MessageBasedRendererV2 {
}); });
} }
private destroy(r: RendererV2) { r.destroy(); }
private destroyNode(r: RendererV2, node: any) {
if (r.destroyNode) {
r.destroyNode(node);
}
this._renderStore.remove(node);
}
private createRenderer(el: any, type: RendererTypeV2, id: number) { private createRenderer(el: any, type: RendererTypeV2, id: number) {
this._renderStore.store(this._rendererFactory.createRenderer(el, type), id); this._renderStore.store(this._rendererFactory.createRenderer(el, type), id);
} }

View File

@ -48,10 +48,10 @@ export class NamedEventEmitter {
} }
} }
const globalEvents = new NamedEventEmitter();
@Injectable() @Injectable()
export class WebWorkerRootRenderer implements RootRenderer { export class WebWorkerRootRenderer implements RootRenderer {
globalEvents = new NamedEventEmitter();
private _messageBroker: ClientMessageBroker; private _messageBroker: ClientMessageBroker;
private _componentRenderers = new Map<string, WebWorkerRenderer>(); private _componentRenderers = new Map<string, WebWorkerRenderer>();
@ -79,7 +79,7 @@ export class WebWorkerRootRenderer implements RootRenderer {
const target = message['eventTarget']; const target = message['eventTarget'];
const event = message['event']; const event = message['event'];
if (target) { if (target) {
globalEvents.dispatchEvent(eventNameWithTarget(target, eventName), event); this.globalEvents.dispatchEvent(eventNameWithTarget(target, eventName), event);
} else { } else {
element.events.dispatchEvent(eventName, event); element.events.dispatchEvent(eventName, event);
} }
@ -277,7 +277,7 @@ export class WebWorkerRenderer implements Renderer {
} }
listenGlobal(target: string, name: string, callback: Function): Function { listenGlobal(target: string, name: string, callback: Function): Function {
globalEvents.listen(eventNameWithTarget(target, name), callback); this._rootRenderer.globalEvents.listen(eventNameWithTarget(target, name), callback);
const unlistenCallbackId = this._rootRenderer.allocateId(); const unlistenCallbackId = this._rootRenderer.allocateId();
this._runOnService('listenGlobal', [ this._runOnService('listenGlobal', [
new FnArg(target), new FnArg(target),
@ -285,7 +285,7 @@ export class WebWorkerRenderer implements Renderer {
new FnArg(unlistenCallbackId), new FnArg(unlistenCallbackId),
]); ]);
return () => { return () => {
globalEvents.unlisten(eventNameWithTarget(target, name), callback); this._rootRenderer.globalEvents.unlisten(eventNameWithTarget(target, name), callback);
this._runOnService('listenDone', [new FnArg(unlistenCallbackId)]); this._runOnService('listenDone', [new FnArg(unlistenCallbackId)]);
}; };
} }
@ -322,6 +322,8 @@ function eventNameWithTarget(target: string, eventName: string): string {
@Injectable() @Injectable()
export class WebWorkerRendererFactoryV2 implements RendererFactoryV2 { export class WebWorkerRendererFactoryV2 implements RendererFactoryV2 {
globalEvents = new NamedEventEmitter();
private _messageBroker: ClientMessageBroker; private _messageBroker: ClientMessageBroker;
constructor( constructor(
@ -359,6 +361,8 @@ export class WebWorkerRendererFactoryV2 implements RendererFactoryV2 {
return result; return result;
} }
freeNode(node: any) { this.renderStore.remove(node); }
allocateId(): number { return this.renderStore.allocateId(); } allocateId(): number { return this.renderStore.allocateId(); }
private _dispatchEvent(message: {[key: string]: any}): void { private _dispatchEvent(message: {[key: string]: any}): void {
@ -370,7 +374,7 @@ export class WebWorkerRendererFactoryV2 implements RendererFactoryV2 {
const event = message['event']; const event = message['event'];
if (target) { if (target) {
globalEvents.dispatchEvent(eventNameWithTarget(target, eventName), event); this.globalEvents.dispatchEvent(eventNameWithTarget(target, eventName), event);
} else { } else {
element.events.dispatchEvent(eventName, event); element.events.dispatchEvent(eventName, event);
} }
@ -380,13 +384,16 @@ export class WebWorkerRendererFactoryV2 implements RendererFactoryV2 {
export class WebWorkerRendererV2 implements RendererV2 { export class WebWorkerRendererV2 implements RendererV2 {
constructor(private _rendererFactory: WebWorkerRendererFactoryV2) {} constructor(private _rendererFactory: WebWorkerRendererFactoryV2) {}
destroyNode: (node: any) => void | null = null;
private asFnArg = new FnArg(this, SerializerTypes.RENDER_STORE_OBJECT); private asFnArg = new FnArg(this, SerializerTypes.RENDER_STORE_OBJECT);
// TODO(vicb): destroy the allocated nodes
destroy(): void { this.callUIWithRenderer('destroy'); } destroy(): void { this.callUIWithRenderer('destroy'); }
destroyNode(node: any) {
this.callUIWithRenderer('destroyNode', [new FnArg(node, SerializerTypes.RENDER_STORE_OBJECT)]);
this._rendererFactory.freeNode(node);
}
createElement(name: string, namespace?: string): any { createElement(name: string, namespace?: string): any {
const node = this._rendererFactory.allocateNode(); const node = this._rendererFactory.allocateNode();
this.callUIWithRenderer('createElement', [ this.callUIWithRenderer('createElement', [
@ -543,7 +550,7 @@ export class WebWorkerRendererV2 implements RendererV2 {
[target, null, null]; [target, null, null];
if (fullName) { if (fullName) {
globalEvents.listen(fullName, listener); this._rendererFactory.globalEvents.listen(fullName, listener);
} else { } else {
targetEl.events.listen(eventName, listener); targetEl.events.listen(eventName, listener);
} }
@ -557,7 +564,7 @@ export class WebWorkerRendererV2 implements RendererV2 {
return () => { return () => {
if (fullName) { if (fullName) {
globalEvents.unlisten(fullName, listener); this._rendererFactory.globalEvents.unlisten(fullName, listener);
} else { } else {
targetEl.events.unlisten(eventName, listener); targetEl.events.unlisten(eventName, listener);
} }

View File

@ -42,7 +42,7 @@ export function createPairedMessageBuses(): PairedMessageBuses {
export function expectBrokerCall( export function expectBrokerCall(
broker: SpyMessageBroker, methodName: string, vals?: Array<any>, broker: SpyMessageBroker, methodName: string, vals?: Array<any>,
handler?: (..._: any[]) => Promise<any>| void): void { handler?: (..._: any[]) => Promise<any>| void): void {
broker.spy('callUI').and.callFake((args: UiArguments, returnType: Type<any>) => { broker.spy('runOnService').and.callFake((args: UiArguments, returnType: Type<any>) => {
expect(args.method).toEqual(methodName); expect(args.method).toEqual(methodName);
if (isPresent(vals)) { if (isPresent(vals)) {
expect(args.args.length).toEqual(vals.length); expect(args.args.length).toEqual(vals.length);

View File

@ -47,7 +47,7 @@ export declare const platformWorkerApp: (extraProviders?: Provider[]) => Platfor
export declare const platformWorkerUi: (extraProviders?: Provider[]) => PlatformRef; export declare const platformWorkerUi: (extraProviders?: Provider[]) => PlatformRef;
/** @experimental */ /** @experimental */
export declare const PRIMITIVE: Type<any>; export declare const PRIMITIVE: SerializerTypes;
/** @experimental */ /** @experimental */
export interface ReceivedMessage { export interface ReceivedMessage {