parent
cd219b92a2
commit
689ded5c47
|
@ -11,15 +11,15 @@ server, etc..
|
|||
|
||||
## Introduction
|
||||
WebWorker support in Angular2 is designed to make it easy to leverage parallelization in your web application.
|
||||
When you choose to run your application in a WebWorker angular runs both your application's logic and the
|
||||
majority of the core angular framework in a WebWorker.
|
||||
When you choose to run your application in a WebWorker angular runs both your application's logic and the
|
||||
majority of the core angular framework in a WebWorker.
|
||||
By offloading as much code as possible to the WebWorker we keep the UI thread
|
||||
free to handle events, manipulate the DOM, and run animations. This provides a
|
||||
better framerate and UX for applications.
|
||||
|
||||
## Bootstrapping a WebWorker Application
|
||||
Bootstrapping a WebWorker application is not much different than bootstrapping a normal application.
|
||||
The primary difference is that you don't pass your root component directly to ```bootstrap```.
|
||||
Bootstrapping a WebWorker application is not much different than bootstrapping a normal application.
|
||||
The primary difference is that you don't pass your root component directly to ```bootstrap```.
|
||||
Instead you pass the name of a background script that calls ```bootstrapWebWorker``` with your root component.
|
||||
|
||||
### Example
|
||||
|
@ -75,11 +75,11 @@ run specifically on the UI or Worker. Additionally, they contain the core messag
|
|||
communicate between the Worker and the UI. This messaging code is not in the standard angular2.js file.
|
||||
* We pass `loader.js` to bootstrap and not `app.ts`. You can think of `loader.js` as the `index.html` of the
|
||||
Worker. Since WebWorkers share no memory with the UI we need to reload the angular2 dependencies before
|
||||
bootstrapping our application. We do this with importScripts. Additionally, we need to do this in a different
|
||||
bootstrapping our application. We do this with `importScripts`. Additionally, we need to do this in a different
|
||||
file than `app.ts` because our module loader (System.js in this example) has not been loaded yet, and `app.ts`
|
||||
will be compiled with a System.define call at the top.
|
||||
will be compiled with a `System.define` call at the top.
|
||||
* The HelloWorld Component looks exactly like a normal Angular2 HelloWorld Component! The goal of WebWorker
|
||||
support was to allow as much of Angular to live in the worker as possible.
|
||||
support was to allow as much of Angular to live in the worker as possible.
|
||||
As such, *most* angular2 components can be bootstrapped in a WebWorker with minimal to no changes required.
|
||||
|
||||
For reference, here's the same HelloWorld example in Dart.
|
||||
|
@ -126,37 +126,37 @@ main(List<String> args, SendPort replyTo) {
|
|||
This code is nearly the same as the TypeScript version with just a couple key differences:
|
||||
* We don't have a `loader.js` file. Dart applications don't need this file because you don't need a module loader.
|
||||
* We pass a `SendPort` to `bootstrapWebWorker`. Dart applications use the Isolate API, which communicates via
|
||||
Dart's Port abstraction. When you call `bootstrap` from the UI thread, angular starts a new Isolate to run
|
||||
your application logic. When Dart starts a new Isolate it passes a `SendPort` to that Isolate so that it
|
||||
can communicate with the Isolate that spawned it. You need to pass this `SendPort` to `bootstrapWebWorker`
|
||||
Dart's Port abstraction. When you call `bootstrap` from the UI thread, angular starts a new Isolate to run
|
||||
your application logic. When Dart starts a new Isolate it passes a `SendPort` to that Isolate so that it
|
||||
can communicate with the Isolate that spawned it. You need to pass this `SendPort` to `bootstrapWebWorker`
|
||||
so that Angular can communicate with the UI.
|
||||
* You need to set up `ReflectionCapabilities` on both the UI and Worker. Just like writing non-concurrent
|
||||
Angular2 Dart applications you need to set up the reflector. You should not use Reflection in production,
|
||||
but should use the angular 2 transformer to remove it in your final JS code. Note there's currently a bug
|
||||
* You need to set up `ReflectionCapabilities` on both the UI and Worker. Just like writing non-concurrent
|
||||
Angular2 Dart applications you need to set up the reflector. You should not use Reflection in production,
|
||||
but should use the angular 2 transformer to remove it in your final JS code. Note there's currently a bug
|
||||
with running the transformer on your UI code (#3971). You can (and should) pass the file where you call
|
||||
`bootstrapWebWorker` as an entry point to the transformer, but you should not pass your UI index file
|
||||
`bootstrapWebWorker` as an entry point to the transformer, but you should not pass your UI index file
|
||||
to the transformer until that bug is fixed.
|
||||
|
||||
## Writing WebWorker Compatible Components
|
||||
You can do almost everything in a WebWorker component that you can do in a typical Angular 2 Component.
|
||||
The main exception is that there is **no** DOM access from a WebWorker component. In Dart this means you can't
|
||||
import anything from `dart:html` and in JavaScript it means you can't use `document` or `window`. Instead you
|
||||
You can do almost everything in a WebWorker component that you can do in a typical Angular 2 Component.
|
||||
The main exception is that there is **no** DOM access from a WebWorker component. In Dart this means you can't
|
||||
import anything from `dart:html` and in JavaScript it means you can't use `document` or `window`. Instead you
|
||||
should use data bindings and if needed you can inject the `Renderer` along with your component's `ElementRef`
|
||||
directly into your component and use methods such as `setElementProperty`, `setElementAttribute`,
|
||||
`setElementClass`, `setElementStyle`, `invokeElementMethod`, and `setText`. Not that you **cannot** call
|
||||
`getNativeElementSync`. Doing so will always return `null` when running in a WebWorker.
|
||||
`setElementClass`, `setElementStyle`, `invokeElementMethod`, and `setText`. Note that you **cannot** call
|
||||
`getNativeElementSync`. Doing so will always return `null` when running in a WebWorker.
|
||||
If you need DOM access see [Running Code on the UI](#running-code-on-the-ui).
|
||||
|
||||
## WebWorker Design Overview
|
||||
When running your application in a WebWorker, the majority of the angular core along with your application logic
|
||||
runs on the worker. The two main components that run on the UI are the `Renderer` and the `RenderCompiler`. When
|
||||
running angular in a WebWorker the bindings for these two components are replaced by the `WebWorkerRenderer` and
|
||||
the `WebWorkerRenderCompiler`. When these components are used at runtime, they pass messages through the
|
||||
running angular in a WebWorker the bindings for these two components are replaced by the `WebWorkerRenderer` and
|
||||
the `WebWorkerRenderCompiler`. When these components are used at runtime, they pass messages through the
|
||||
[MessageBroker](#messagebroker) instructing the UI to run the actual method and return the result. The
|
||||
[MessageBroker](#messagebroker) abstraction allows either side of the WebWorker boundary to schedule code to run
|
||||
on the opposite side and receive the result. You can use the [MessageBroker](#messagebroker)
|
||||
Additionally, the [MessageBroker](#messagebroker) sits on top of the [MessageBus](#messagebus).
|
||||
MessageBus is a low level abstraction that provides a language agnostic API for communicating with angular components across any runtime boundary such as `WebWorker <--> UI` communication, `UI <--> Server` communication,
|
||||
Additionally, the [MessageBroker](#messagebroker) sits on top of the [MessageBus](#messagebus).
|
||||
MessageBus is a low level abstraction that provides a language agnostic API for communicating with angular components across any runtime boundary such as `WebWorker <--> UI` communication, `UI <--> Server` communication,
|
||||
or `Window <--> Window` communication.
|
||||
|
||||
See the diagram below for a high level overview of how this code is structured:
|
||||
|
@ -164,9 +164,9 @@ See the diagram below for a high level overview of how this code is structured:
|
|||
![WebWorker Diagram](http://stanford.edu/~jteplitz/ng_2_worker.png)
|
||||
|
||||
## Running Code on the UI
|
||||
If your application needs to run code on the UI, there are a few options. The easiest way is to use a
|
||||
If your application needs to run code on the UI, there are a few options. The easiest way is to use a
|
||||
CustomElement in your view. You can then register this custom element from your html file and run code in response
|
||||
to the element's lifecycle hooks. Note, Custom Elements are still experimental. See
|
||||
to the element's lifecycle hooks. Note, Custom Elements are still experimental. See
|
||||
[MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements) for the latest details on how
|
||||
to use them.
|
||||
|
||||
|
@ -192,8 +192,8 @@ application running on a server. It's intended to be used with either the `Singl
|
|||
application to run on a server and communicate with a single browser that's running the `WebSocketMessageBus`.
|
||||
3. The `MultiClientServerMessageBus` is like the `SingleClientServerMessageBus` except it allows an arbitrary
|
||||
number of clients to connect to the server. It keeps all connected browsers in sync and if an event fires in
|
||||
any connected browser it propagates the result to all connected clients. This can be especially useful as a
|
||||
debugging tool, by allowing you to connect multiple browsers / devices to the same angular application,
|
||||
any connected browser it propagates the result to all connected clients. This can be especially useful as a
|
||||
debugging tool, by allowing you to connect multiple browsers / devices to the same angular application,
|
||||
change the state of that application, and ensure that all the clients render the view correctly. Using these tools
|
||||
can make it easy to catch tricky browser compatibility issues.
|
||||
|
||||
|
@ -277,9 +277,9 @@ class MyComponent {
|
|||
The only substantial difference between these APIs in Dart and TypeScript is the different APIs for the
|
||||
`EventEmitter`.
|
||||
|
||||
**Note** Because the messages passed through the MessageBus cross a WebWorker boundary, they must be serializable.
|
||||
**Note:** Because the messages passed through the MessageBus cross a WebWorker boundary, they must be serializable.
|
||||
If you use the MessageBus directly, you are responsible for serializing your messages.
|
||||
In JavaScript / TypeScript this means they must be serializable via JavaScript's
|
||||
In JavaScript / TypeScript this means they must be serializable via JavaScript's
|
||||
[structured cloning algorithim](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
|
||||
|
||||
In Dart this means they must be valid messages that can be passed through a
|
||||
|
@ -287,26 +287,26 @@ In Dart this means they must be valid messages that can be passed through a
|
|||
|
||||
|
||||
### MessageBus and Zones
|
||||
The MessageBus API includes support for [zones](http://www.github.com/angular/zone.js).
|
||||
The MessageBus API includes support for [zones](http://www.github.com/angular/zone.js).
|
||||
A MessageBus can be attached to a specific zone (by calling `attachToZone`). Then specific channels can be
|
||||
specified to run in the zone when they are initialized.
|
||||
If a channel is running in the zone, that means that any events emitted from that channel will be executed within
|
||||
the given zone. For example, by default angular runs the EventDispatch channel inside the angular zone. That means
|
||||
when an event is fired from the DOM and received on the WebWorker the event handler automatically runs inside the
|
||||
angular zone. This is desired because after the event handler exits we want to exit the zone so that we trigger
|
||||
change detection. Generally, you want your channels to run inside the zone unless you have a good reason for why
|
||||
change detection. Generally, you want your channels to run inside the zone unless you have a good reason for why
|
||||
they need to run outside the zone.
|
||||
|
||||
### Implementing and Using a Custom MessageBus
|
||||
**Note** Implementing and using a Custom MesageBus is experimental and requires importing from private APIs.
|
||||
**Note:** Implementing and using a Custom MessageBus is experimental and requires importing from private APIs.
|
||||
|
||||
If you want to drive your application from something other than a WebWorker you can implement a custom message
|
||||
bus. Implementing a custom message bus just means creating a class that fulfills the API specified by the
|
||||
abstract MessageBus class.
|
||||
|
||||
If you're implementing your MessageBus in Dart you can extend the `GenericMessageBus` class included in angular.
|
||||
if you do this, you don't need to implement zone or channel support yourself. You only need to implement a
|
||||
`MessageBusSink` that extends `GenericMessageBusSink` and a `MessageBusSource` that extends
|
||||
if you do this, you don't need to implement zone or channel support yourself. You only need to implement a
|
||||
`MessageBusSink` that extends `GenericMessageBusSink` and a `MessageBusSource` that extends
|
||||
`GenericMessageBusSource`. The `MessageBusSink` must override the `sendMessages` method. This method is
|
||||
given a list of serialized messages that it is required to send through the sink.
|
||||
The `MessageBusSource` needs to provide a [Stream](https://api.dartlang.org/1.12.1/dart-async/Stream-class.html)
|
||||
|
@ -330,7 +330,7 @@ class JsonMessageBusSink extends GenericMessageBusSink {
|
|||
|
||||
class JsonMessageBusSource extends GenericMessageBuSource {
|
||||
JsonMessageBusSource(Stream incomingMessages) : super (incomingMessages);
|
||||
|
||||
|
||||
@override
|
||||
List<dynamic> decodeMessages(dynamic messages) {
|
||||
return JSON.decode(messages);
|
||||
|
@ -338,7 +338,7 @@ class JsonMessageBusSource extends GenericMessageBuSource {
|
|||
}
|
||||
```
|
||||
|
||||
Once you've implemented your custom MessageBus in either TypeScript or Dart you can tell angular to use it like
|
||||
Once you've implemented your custom MessageBus in either TypeScript or Dart, you can tell angular to use it like
|
||||
so:
|
||||
In TypeScript:
|
||||
```TypeScript
|
||||
|
@ -381,7 +381,7 @@ main() {
|
|||
}
|
||||
```
|
||||
Notice how we call `bootstrapUICommon` instead of `bootstrap` from the UI side. `bootstrap` spans a new WebWorker
|
||||
/ Isolate and attaches the default angular MessageBus to it. If you're using a custom MessageBus you are
|
||||
/ Isolate and attaches the default angular MessageBus to it. If you're using a custom MessageBus you are
|
||||
responsible for setting up the application side and initiating communication with it. `bootstrapUICommon` assumes
|
||||
that the given MessageBus is already set up and can communicate with the application.
|
||||
Similarly, we call `bootstrapWebWorkerCommon` instead of `boostrapWebWorker` from the application side. This is
|
||||
|
@ -390,8 +390,11 @@ because `bootstrapWebWorker` assumes you're using the default angular MessageBus
|
|||
## MessageBroker
|
||||
The MessageBroker is a higher level messaging abstraction that sits on top of the MessageBus. It is used when you
|
||||
want to execute code on the other side of a runtime boundary and may want to receive the result.
|
||||
There are two types of MessageBrokers. The `ServiceMessageBroker` is used by the side that actually performs
|
||||
an operation and may return a result. Conversely, the `ClientMessageBroker` is used by the side that requests that
|
||||
There are two types of MessageBrokers:
|
||||
|
||||
1. The `ServiceMessageBroker` is used by the side that actually performs
|
||||
an operation and may return a result;
|
||||
2. The `ClientMessageBroker` is used by the side that requests that
|
||||
an operation be performed and may want to receive the result.
|
||||
|
||||
### Using the MessageBroker In Your Application
|
||||
|
@ -422,7 +425,7 @@ from 'angular2/web_worker/worker';
|
|||
export class MyComponent {
|
||||
constructor(brokerFactory: ClientMessageBrokerFactory) {
|
||||
var broker = brokerFactory.createMessageBroker("My Broker Channel");
|
||||
|
||||
|
||||
var arguments = [new FnArg(value, PRIMITIVE)];
|
||||
var methodInfo = new UiArguments("awesomeMethod", arguments);
|
||||
broker.runOnService(methodInfo, PRIMTIVE).then((result: string) => {
|
||||
|
@ -439,7 +442,7 @@ import 'package:angular2/web_worker/ui.dart';
|
|||
main() {
|
||||
var instance = bootstrap("background.dart");
|
||||
var broker = instance.app.createServiceMessageBroker("My Broker Channel");
|
||||
|
||||
|
||||
// assume we have some function doCoolThings that takes a String argument and returns a Future<String>
|
||||
broker.registerMethod("awesomeMethod", [PRIMITIVE], (String arg1) => doCoolThing(arg1), PRIMITIVE);
|
||||
}
|
||||
|
@ -454,7 +457,7 @@ import 'package:angular2/web_worker/worker.dart';
|
|||
class MyComponent {
|
||||
MyComponent(ClientMessageBrokerFactory brokerFactory) {
|
||||
var broker = brokerFactory.createMessageBroker("My Broker Channel");
|
||||
|
||||
|
||||
var arguments = [new FnArg(value, PRIMITIVE)];
|
||||
var methodInfo = new UiArguments("awesomeMethod", arguments);
|
||||
broker.runOnService(methodInfo, PRIMTIVE).then((String result) {
|
||||
|
@ -472,5 +475,5 @@ However, at the moment the serializer only knows how to serialize angular classe
|
|||
If you're passing anything other than those types around in your application you can handle serialization yourself
|
||||
and then use the `PRIMITIVE` type to tell the MessageBroker to avoid serializing your data.
|
||||
|
||||
The last thing that happens is that the client calls `runOnService` with the name of the method it wants to run,
|
||||
The last thing that happens is that the client calls `runOnService` with the name of the method it wants to run,
|
||||
a list of that method's arguments and their types, and (optionally) the expected return type.
|
||||
|
|
Loading…
Reference in New Issue