312 lines
7.5 KiB
Markdown
312 lines
7.5 KiB
Markdown
|
# Zone.js
|
||
|
|
||
|
[![Build Status](https://travis-ci.org/angular/zone.js.png)](https://travis-ci.org/angular/zone.js)
|
||
|
|
||
|
Implements _Zones_ for JavaScript, inspired by [Dart](https://www.dartlang.org/articles/zones/).
|
||
|
|
||
|
# NEW Zone.js POST-v0.6.0
|
||
|
|
||
|
See the new API [here](./dist/zone.js.d.ts).
|
||
|
|
||
|
# DEPRECATED Zone.js PRE-v0.5.15
|
||
|
|
||
|
## What's a Zone?
|
||
|
|
||
|
A Zone is an execution context that persists across async tasks.
|
||
|
You can think of it as [thread-local storage](http://en.wikipedia.org/wiki/Thread-local_storage) for JavaScript VMs.
|
||
|
|
||
|
See this video from ng-conf 2014 for a detailed explanation:
|
||
|
|
||
|
[![screenshot of the zone.js presentation and ng-conf 2014](/presentation.png)](//www.youtube.com/watch?v=3IqtmUscE_U)
|
||
|
|
||
|
### Running Within a Zone
|
||
|
|
||
|
You can run code within a zone with `zone.run`.
|
||
|
Tasks scheduled (with `setTimeout`, `setInterval`, or event listeners) stay within that zone.
|
||
|
|
||
|
```javascript
|
||
|
zone.fork().run(function () {
|
||
|
zone.inTheZone = true;
|
||
|
|
||
|
setTimeout(function () {
|
||
|
console.log('in the zone: ' + !!zone.inTheZone);
|
||
|
}, 0);
|
||
|
});
|
||
|
|
||
|
console.log('in the zone: ' + !!zone.inTheZone);
|
||
|
```
|
||
|
|
||
|
The above will log:
|
||
|
|
||
|
```
|
||
|
'in the zone: false'
|
||
|
'in the zone: true'
|
||
|
```
|
||
|
|
||
|
Note that the function delayed by `setTimeout` stays inside the zone.
|
||
|
|
||
|
### Forking a Zone
|
||
|
|
||
|
Zones have a set of hooks that allow you to change the behavior of code running within that zone.
|
||
|
To change a zone, you _fork_ it to get a new one.
|
||
|
|
||
|
```javascript
|
||
|
zone.fork({
|
||
|
beforeTask: function () {
|
||
|
console.log('hi');
|
||
|
}
|
||
|
}).run(function () {
|
||
|
// do stuff
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Hooks that you don't override when forking a zone are inherited from the existing one.
|
||
|
|
||
|
See the [API docs](#api) below for more.
|
||
|
|
||
|
|
||
|
## Usage
|
||
|
|
||
|
To start using Zones, you need to include the `zone.js` script in this package onto
|
||
|
your page. This script should appear in the `<head>` of your HTML file before any other
|
||
|
scripts, including shims/polyfills.
|
||
|
|
||
|
|
||
|
## Examples
|
||
|
|
||
|
There are two kinds of examples:
|
||
|
|
||
|
1. The kind you have to run
|
||
|
2. Illustrative code snippets in this README
|
||
|
|
||
|
### Running the ones that you have to run
|
||
|
|
||
|
For fully working examples:
|
||
|
|
||
|
1. Spawn a webserver in the root of the directory in which this repo lives.
|
||
|
(I like to use `python -m SimpleHTTPServer 3000`).
|
||
|
2. Open `http://localhost:3000/example` in your browser
|
||
|
|
||
|
Below are the aforementioned snippets.
|
||
|
|
||
|
### Overriding A Zone's Hook
|
||
|
|
||
|
```javascript
|
||
|
var someZone = zone.fork({
|
||
|
afterTask: function () {
|
||
|
console.log('goodbye');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
someZone.fork({
|
||
|
afterTask: function () {
|
||
|
console.log('cya l8r');
|
||
|
}
|
||
|
}).run(function () {
|
||
|
// do stuff
|
||
|
});
|
||
|
|
||
|
// logs: cya l8r
|
||
|
```
|
||
|
|
||
|
### Augmenting A Zone's Hook
|
||
|
|
||
|
When you fork a zone, you'll often want to control how the parent zone's
|
||
|
hook gets called.
|
||
|
|
||
|
Prefixing a hook with `$` means that the hook will be passed the
|
||
|
parent zone's hook, and the hook will be expected to return the function to
|
||
|
be invoked rather than be the function itself.
|
||
|
|
||
|
```javascript
|
||
|
var someZone = zone.fork({
|
||
|
afterTask: function () {
|
||
|
console.log('goodbye');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
someZone.fork({
|
||
|
$afterTask: function (parentOnLeave) {
|
||
|
// return the hook
|
||
|
return function afterTask() {
|
||
|
parentOnLeave();
|
||
|
console.log('cya l8r');
|
||
|
};
|
||
|
}
|
||
|
}).run(function () {
|
||
|
// do stuff
|
||
|
});
|
||
|
|
||
|
// logs: goodbye
|
||
|
// cya l8r
|
||
|
```
|
||
|
|
||
|
#### `+` and `-` Sugar
|
||
|
Most of the time, you'll want to run a hook before or after the parent's implementation.
|
||
|
You can prefix a hook with `-` for running before, and `+` for running after.
|
||
|
|
||
|
The above can be written like this:
|
||
|
|
||
|
```javascript
|
||
|
var someZone = zone.fork({
|
||
|
afterTask: function () {
|
||
|
console.log('goodbye');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
someZone.fork({
|
||
|
'+afterTask': function () {
|
||
|
console.log('cya l8r');
|
||
|
}
|
||
|
}).run(function () {
|
||
|
// do stuff
|
||
|
});
|
||
|
|
||
|
// logs: goodbye
|
||
|
// cya l8r
|
||
|
```
|
||
|
|
||
|
This frees you from writing boilerplate to compose a new hook.
|
||
|
|
||
|
## API
|
||
|
|
||
|
Zone.js exports a single object: `window.zone`.
|
||
|
|
||
|
### `zone.run`
|
||
|
|
||
|
Runs a given function within the zone.
|
||
|
Explained above.
|
||
|
|
||
|
### `zone.bind`
|
||
|
|
||
|
Transforms a function to run within the given zone.
|
||
|
|
||
|
### `zone.fork`
|
||
|
|
||
|
```javascript
|
||
|
var myZone = zone.fork({
|
||
|
onZoneCreated: function () {},
|
||
|
beforeTask: function () {},
|
||
|
afterTask: function () {},
|
||
|
onError: function () {},
|
||
|
enqueueTask: function() {},
|
||
|
dequeueTask: function() {},
|
||
|
setTimeout: function () {},
|
||
|
setInterval: function () {},
|
||
|
alert: function () {},
|
||
|
prompt: function () {},
|
||
|
});
|
||
|
myZone.run(function () {
|
||
|
// woo!
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Below describes the behavior of each of these hooks.
|
||
|
|
||
|
### `zone.onZoneCreated`
|
||
|
|
||
|
Runs when a zone is forked.
|
||
|
|
||
|
### `zone.beforeTask`
|
||
|
|
||
|
Before a function invoked with `zone.run`, this hook runs.
|
||
|
If `zone.beforeTask` throws, the function passed to `run` will not be invoked.
|
||
|
|
||
|
### `zone.afterTask`
|
||
|
|
||
|
After a function in a zone runs, the `afterTask` hook runs.
|
||
|
This hook will run even if the function passed to `run` throws.
|
||
|
|
||
|
### `zone.onError`
|
||
|
|
||
|
This hook is called when the function passed to `run` or the `beforeTask` hook throws.
|
||
|
|
||
|
### `zone.enqueueTask`
|
||
|
|
||
|
This hook is called when a function is registered with the VM.
|
||
|
For instance `setTimeout` and `addEventListener`.
|
||
|
|
||
|
### `zone.dequeueTask`
|
||
|
|
||
|
This hook is called when a function is unregistered with the VM.
|
||
|
For instance `clearTimeout` and `removeEventListener`.
|
||
|
|
||
|
### `zone.setTimeout`, `zone.setInterval`, `zone.alert`, `zone.prompt`
|
||
|
|
||
|
These hooks allow you to change the behavior of `window.setTimeout`, `window.setInterval`, etc.
|
||
|
While in this zone, calls to `window.setTimeout` will redirect to `zone.setTimeout`.
|
||
|
|
||
|
### `zone.requestAnimationFrame`, `zone.webkitRequestAnimationFrame`, `zone.mozRequestAnimationFrame`
|
||
|
|
||
|
These hooks allow you to change the behavior of `window.requestAnimationFrame()`,
|
||
|
`window.webkitRequestAnimationFrame`, and `window.mozRequestAnimationFrame`.
|
||
|
|
||
|
By default the wrapCallback is executed in the zone where those methods have been called to avoid
|
||
|
growing the stack size on each recursive call.
|
||
|
|
||
|
### `zone.addEventListener`
|
||
|
|
||
|
This hook allows you to intercept calls to `EventTarget#addEventListener`.
|
||
|
|
||
|
````javascript
|
||
|
var clickListenerCount = 0;
|
||
|
|
||
|
zone.fork(
|
||
|
$addEventListener: function(parentAddEventListener) {
|
||
|
return function (type, listener) {
|
||
|
if (type === 'click') clickListenerCount++;
|
||
|
return parentAddEventListener.apply(this, arguments);
|
||
|
};
|
||
|
}
|
||
|
);
|
||
|
|
||
|
zone.run(function() {
|
||
|
myElement.addEventListener('click', listener);
|
||
|
myOtherElement.addEventListener('click', listener);
|
||
|
|
||
|
console.log(clickListenerCount); // 2
|
||
|
});
|
||
|
````
|
||
|
|
||
|
### `zone.removeEventListener`
|
||
|
|
||
|
This hook allows you to intercept calls to `EventTarget#removeEventListener`.
|
||
|
|
||
|
````javascript
|
||
|
var clickListenerCount = 0;
|
||
|
|
||
|
zone.fork(
|
||
|
$removeEventListener: function(parentRemoveEventListener) {
|
||
|
return function (type, listener) {
|
||
|
if (type === 'click') clickListenerCount--;
|
||
|
return parentRemoveEventListener.apply(this, arguments);
|
||
|
};
|
||
|
}
|
||
|
);
|
||
|
|
||
|
zone.run(function() {
|
||
|
myElement.addEventListener('click', listener);
|
||
|
myElement.removeEventListener('click', listener);
|
||
|
|
||
|
console.log(clickListenerCount); // 0
|
||
|
});
|
||
|
````
|
||
|
|
||
|
|
||
|
## Status
|
||
|
|
||
|
* `setTimeout`, `setInterval`, and `addEventListener` work in FF23, IE10, and Chrome.
|
||
|
* stack trace rewrite is kinda ugly and may contain extraneous calls.
|
||
|
* `elt.onevent` works in FF23, IE10, but not Chrome. There's [a fix in the works though](https://code.google.com/p/chromium/issues/detail?id=43394)!
|
||
|
|
||
|
|
||
|
## See also
|
||
|
* [async-listener](https://github.com/othiym23/async-listener) - a similar library for node
|
||
|
* [Async stack traces in Chrome](http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/)
|
||
|
* [strongloop/zone](https://github.com/strongloop/zone)
|
||
|
* [vizone](https://github.com/gilbox/vizone) - control flow visualizer that uses zone.js
|
||
|
|
||
|
|
||
|
## License
|
||
|
MIT
|