doc(web_workers): fixes some typos and adjusts formatting

Closes #4425
This commit is contained in:
Eric Mendes Dantas 2015-09-30 08:44:28 -03:00 committed by Misko Hevery
parent cd219b92a2
commit 689ded5c47
1 changed files with 46 additions and 43 deletions

View File

@ -11,15 +11,15 @@ server, etc..
## Introduction ## Introduction
WebWorker support in Angular2 is designed to make it easy to leverage parallelization in your web application. 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 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. majority of the core angular framework in a WebWorker.
By offloading as much code as possible to the WebWorker we keep the UI thread 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 free to handle events, manipulate the DOM, and run animations. This provides a
better framerate and UX for applications. better framerate and UX for applications.
## Bootstrapping a WebWorker Application ## Bootstrapping a WebWorker Application
Bootstrapping a WebWorker application is not much different than bootstrapping a normal 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```. 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. Instead you pass the name of a background script that calls ```bootstrapWebWorker``` with your root component.
### Example ### 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. 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 * 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 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` 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 * 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. 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. 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: 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 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 * 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 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 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` can communicate with the Isolate that spawned it. You need to pass this `SendPort` to `bootstrapWebWorker`
so that Angular can communicate with the UI. 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 * 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, 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 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 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. to the transformer until that bug is fixed.
## Writing WebWorker Compatible Components ## Writing WebWorker Compatible Components
You can do almost everything in a WebWorker component that you can do in a typical Angular 2 Component. 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 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 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` 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`, directly into your component and use methods such as `setElementProperty`, `setElementAttribute`,
`setElementClass`, `setElementStyle`, `invokeElementMethod`, and `setText`. Not that you **cannot** call `setElementClass`, `setElementStyle`, `invokeElementMethod`, and `setText`. Note that you **cannot** call
`getNativeElementSync`. Doing so will always return `null` when running in a WebWorker. `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). If you need DOM access see [Running Code on the UI](#running-code-on-the-ui).
## WebWorker Design Overview ## WebWorker Design Overview
When running your application in a WebWorker, the majority of the angular core along with your application logic 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 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 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 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) 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 [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) 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). 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, 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. or `Window <--> Window` communication.
See the diagram below for a high level overview of how this code is structured: 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) ![WebWorker Diagram](http://stanford.edu/~jteplitz/ng_2_worker.png)
## Running Code on the UI ## 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 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 [MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements) for the latest details on how
to use them. 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`. 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 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 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 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, 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 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. 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 The only substantial difference between these APIs in Dart and TypeScript is the different APIs for the
`EventEmitter`. `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. 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). [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 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 ### 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 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. 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 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 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 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 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. they need to run outside the zone.
### Implementing and Using a Custom MessageBus ### 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 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 bus. Implementing a custom message bus just means creating a class that fulfills the API specified by the
abstract MessageBus class. abstract MessageBus class.
If you're implementing your MessageBus in Dart you can extend the `GenericMessageBus` class included in angular. 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 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 `MessageBusSink` that extends `GenericMessageBusSink` and a `MessageBusSource` that extends
`GenericMessageBusSource`. The `MessageBusSink` must override the `sendMessages` method. This method is `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. 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) 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 { class JsonMessageBusSource extends GenericMessageBuSource {
JsonMessageBusSource(Stream incomingMessages) : super (incomingMessages); JsonMessageBusSource(Stream incomingMessages) : super (incomingMessages);
@override @override
List<dynamic> decodeMessages(dynamic messages) { List<dynamic> decodeMessages(dynamic messages) {
return JSON.decode(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: so:
In TypeScript: In TypeScript:
```TypeScript ```TypeScript
@ -381,7 +381,7 @@ main() {
} }
``` ```
Notice how we call `bootstrapUICommon` instead of `bootstrap` from the UI side. `bootstrap` spans a new WebWorker 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 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. 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 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 ## MessageBroker
The MessageBroker is a higher level messaging abstraction that sits on top of the MessageBus. It is used when you 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. 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 There are two types of MessageBrokers:
an operation and may return a result. Conversely, the `ClientMessageBroker` is used by the side that requests that
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. an operation be performed and may want to receive the result.
### Using the MessageBroker In Your Application ### Using the MessageBroker In Your Application
@ -422,7 +425,7 @@ from 'angular2/web_worker/worker';
export class MyComponent { export class MyComponent {
constructor(brokerFactory: ClientMessageBrokerFactory) { constructor(brokerFactory: ClientMessageBrokerFactory) {
var broker = brokerFactory.createMessageBroker("My Broker Channel"); var broker = brokerFactory.createMessageBroker("My Broker Channel");
var arguments = [new FnArg(value, PRIMITIVE)]; var arguments = [new FnArg(value, PRIMITIVE)];
var methodInfo = new UiArguments("awesomeMethod", arguments); var methodInfo = new UiArguments("awesomeMethod", arguments);
broker.runOnService(methodInfo, PRIMTIVE).then((result: string) => { broker.runOnService(methodInfo, PRIMTIVE).then((result: string) => {
@ -439,7 +442,7 @@ import 'package:angular2/web_worker/ui.dart';
main() { main() {
var instance = bootstrap("background.dart"); var instance = bootstrap("background.dart");
var broker = instance.app.createServiceMessageBroker("My Broker Channel"); var broker = instance.app.createServiceMessageBroker("My Broker Channel");
// assume we have some function doCoolThings that takes a String argument and returns a Future<String> // 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); broker.registerMethod("awesomeMethod", [PRIMITIVE], (String arg1) => doCoolThing(arg1), PRIMITIVE);
} }
@ -454,7 +457,7 @@ import 'package:angular2/web_worker/worker.dart';
class MyComponent { class MyComponent {
MyComponent(ClientMessageBrokerFactory brokerFactory) { MyComponent(ClientMessageBrokerFactory brokerFactory) {
var broker = brokerFactory.createMessageBroker("My Broker Channel"); var broker = brokerFactory.createMessageBroker("My Broker Channel");
var arguments = [new FnArg(value, PRIMITIVE)]; var arguments = [new FnArg(value, PRIMITIVE)];
var methodInfo = new UiArguments("awesomeMethod", arguments); var methodInfo = new UiArguments("awesomeMethod", arguments);
broker.runOnService(methodInfo, PRIMTIVE).then((String result) { 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 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. 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. a list of that method's arguments and their types, and (optionally) the expected return type.