# 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/).

> If you're using zone.js via unpkg please provide a query param `?main=browser`  
`https://unpkg.com/zone.js?main=browser`

# 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.current.fork({}).run(function () {
    Zone.current.inTheZone = true;

    setTimeout(function () {
        console.log('in the zone: ' + !!Zone.current.inTheZone);
    }, 0);
});

console.log('in the zone: ' + !!Zone.current.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
