angular2
Version:
Angular 2 - a web framework for modern web apps
168 lines (149 loc) • 5.14 kB
text/typescript
import {MessageBus} from "angular2/src/web_workers/shared/message_bus";
import {
print,
isPresent,
DateWrapper,
stringify,
Type,
StringWrapper
} from "angular2/src/facade/lang";
import {
PromiseCompleter,
PromiseWrapper,
ObservableWrapper,
EventEmitter
} from "angular2/src/facade/async";
import {StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
import {Serializer} from "angular2/src/web_workers/shared/serializer";
import {Injectable} from "angular2/src/core/di";
export {Type} from "angular2/src/facade/lang";
export abstract class ClientMessageBrokerFactory {
/**
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
*/
abstract createMessageBroker(channel: string, runInZone?: boolean): ClientMessageBroker;
}
export class ClientMessageBrokerFactory_ extends ClientMessageBrokerFactory {
/** @internal */
public _serializer: Serializer;
constructor(private _messageBus: MessageBus, _serializer: Serializer) {
super();
this._serializer = _serializer;
}
/**
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
*/
createMessageBroker(channel: string, runInZone: boolean = true): ClientMessageBroker {
this._messageBus.initChannel(channel, runInZone);
return new ClientMessageBroker_(this._messageBus, this._serializer, channel);
}
}
export abstract class ClientMessageBroker {
abstract runOnService(args: UiArguments, returnType: Type): Promise<any>;
}
export class ClientMessageBroker_ extends ClientMessageBroker {
private _pending: Map<string, PromiseCompleter<any>> = new Map<string, PromiseCompleter<any>>();
private _sink: EventEmitter<any>;
/** @internal */
public _serializer: Serializer;
constructor(messageBus: MessageBus, _serializer: Serializer, public channel) {
super();
this._sink = messageBus.to(channel);
this._serializer = _serializer;
var source = messageBus.from(channel);
ObservableWrapper.subscribe(source,
(message: {[key: string]: any}) => this._handleMessage(message));
}
private _generateMessageId(name: string): string {
var time: string = stringify(DateWrapper.toMillis(DateWrapper.now()));
var iteration: number = 0;
var id: string = name + time + stringify(iteration);
while (isPresent(this._pending[id])) {
id = `${name}${time}${iteration}`;
iteration++;
}
return id;
}
runOnService(args: UiArguments, returnType: Type): Promise<any> {
var fnArgs = [];
if (isPresent(args.args)) {
args.args.forEach(argument => {
if (argument.type != null) {
fnArgs.push(this._serializer.serialize(argument.value, argument.type));
} else {
fnArgs.push(argument.value);
}
});
}
var promise: Promise<any>;
var id: string = null;
if (returnType != null) {
var completer: PromiseCompleter<any> = PromiseWrapper.completer();
id = this._generateMessageId(args.method);
this._pending.set(id, completer);
PromiseWrapper.catchError(completer.promise, (err, stack?) => {
print(err);
completer.reject(err, stack);
});
promise = PromiseWrapper.then(completer.promise, (value: any) => {
if (this._serializer == null) {
return value;
} else {
return this._serializer.deserialize(value, returnType);
}
});
} else {
promise = null;
}
// TODO(jteplitz602): Create a class for these messages so we don't keep using StringMap #3685
var message = {'method': args.method, 'args': fnArgs};
if (id != null) {
message['id'] = id;
}
ObservableWrapper.callEmit(this._sink, message);
return promise;
}
private _handleMessage(message: {[key: string]: any}): void {
var data = new MessageData(message);
// TODO(jteplitz602): replace these strings with messaging constants #3685
if (StringWrapper.equals(data.type, "result") || StringWrapper.equals(data.type, "error")) {
var id = data.id;
if (this._pending.has(id)) {
if (StringWrapper.equals(data.type, "result")) {
this._pending.get(id).resolve(data.value);
} else {
this._pending.get(id).reject(data.value, null);
}
this._pending.delete(id);
}
}
}
}
class MessageData {
type: string;
value: any;
id: string;
constructor(data: {[key: string]: any}) {
this.type = StringMapWrapper.get(data, "type");
this.id = this._getValueIfPresent(data, "id");
this.value = this._getValueIfPresent(data, "value");
}
/**
* Returns the value from the StringMap if present. Otherwise returns null
* @internal
*/
_getValueIfPresent(data: {[key: string]: any}, key: string) {
if (StringMapWrapper.contains(data, key)) {
return StringMapWrapper.get(data, key);
} else {
return null;
}
}
}
export class FnArg {
constructor(public value, public type: Type) {}
}
export class UiArguments {
constructor(public method: string, public args?: FnArg[]) {}
}