What is zone.js
and
how does it work with Angular?

David Stanich

https://github.com/dstanich/zonejs-presentation

About Me

David Stanich

| Github: dstanich | Twitter: @dstanich |
  • Software Engineer @ IBM Watson Health
  • AngularJS: several years with multiple apps
  • Angular: migration started this year

Angular 101


# Install angular-cli
npm install -g @angular/cli

# Create brand new project
ng new my-new-project

# What did that command just do?
cat package.json
            

Angular stuff - makes sense


"dependencies": {
  "@angular/animations": "^4.2.4",
  "@angular/common": "^4.2.4",
  "@angular/compiler": "^4.2.4",
  "@angular/core": "^4.2.4",
  "@angular/forms": "^4.2.4",
  "@angular/http": "^4.2.4",
  "@angular/platform-browser": "^4.2.4",
  "@angular/platform-browser-dynamic": "^4.2.4",
  "@angular/router": "^4.2.4",
  "core-js": "^2.4.1",
  "rxjs": "^5.4.2",
  "zone.js": "^0.8.14"
},
            

Libraries - Seem familiar


"dependencies": {
  "@angular/animations": "^4.2.4",
  "@angular/common": "^4.2.4",
  "@angular/compiler": "^4.2.4",
  "@angular/core": "^4.2.4",
  "@angular/forms": "^4.2.4",
  "@angular/http": "^4.2.4",
  "@angular/platform-browser": "^4.2.4",
  "@angular/platform-browser-dynamic": "^4.2.4",
  "@angular/router": "^4.2.4",
  "core-js": "^2.4.1",
  "rxjs": "^5.4.2",
  "zone.js": "^0.8.14"
},
            

zone.js - What the heck is that?


"dependencies": {
  "@angular/animations": "^4.2.4",
  "@angular/common": "^4.2.4",
  "@angular/compiler": "^4.2.4",
  "@angular/core": "^4.2.4",
  "@angular/forms": "^4.2.4",
  "@angular/http": "^4.2.4",
  "@angular/platform-browser": "^4.2.4",
  "@angular/platform-browser-dynamic": "^4.2.4",
  "@angular/router": "^4.2.4",
  "core-js": "^2.4.1",
  "rxjs": "^5.4.2",
  "zone.js": "^0.8.14"
},
            

So why is it there?

Short answer:
Change detection
Keep this in mind over the next slides

Key Browser Concepts

  • Async events and tasks
  • Monkey patching functions

zone.js

A Zone is an execution context that persists across async tasks, and allows the creator of the zone to observe and control execution of the code within the zone.
- Zone Primer Doc
Proposed for EcmaScript, currently blocked
| zone.js Github| zone.js API | Zone Primer Doc |

What can it do?

  • Maps all async events into tasks
  • Create new (forked) zones and manage context
  • Control what zone code runs inside
  • Observe starting, stopping, and scheduling of tasks

Event Types

Type Usage Example
Microtask Guaranteed to run once and immediately promise.then()
Macrotask Work to be done later that is not guaranteed to run. Able to cancel. setTimeout, setInterval, requestAnimationFrame
EventTask Task may execute zero or more times with an unknown delay MouseClick, KeyboardEvent, 'on' properties, ...

Managing zones

  • Always a root zone by default
  • Ability to create new child zones
  • Store shallow immutable properties
API Usage
Zone.current Returns currently active zone
zoneObj.fork(ZoneSpec) Create a new child zone. ZoneSpec
zoneObj.get(key) Returns value of property with given key
zoneObj.getZoneWith(key) Searches parents for zone with given key

Controlling what runs in a zone

  • Execute/schedule code within a specific zone
  • Segments code so it's easier to control
  • Consolidated error handling
API Usage
zoneObj.run(fn) Run function within zoneObj
zoneObj.runGuarded(fn) Run function within zoneObj and catch all exceptions
zoneObj.wrap(fn) Returns function wrapping fn and restoring zone and this

Observing with zone hooks

Part of the ZoneSpec. Define handlers for what you need.
Hook Usage
onFork Called when the zone is forked.
onIntercept Called when wrap() is called
onInvoke Called when run() callback is executed
onHandleError Called when an error is handled by zone
onScheduleTask Called when a task is scheduled
onInvokeTask Called when a scheduled task is run
onCancelTask Called when a scheduled task is canceled
onHasTask Called when a task queue is modified

zone.js Demos

Source - js-examples/

Angular and zone.js

Angular uses zone.js for intelligently kicking off change detection on components.
State is changed in applications by async tasks. zone.js allows Angular to detect and react to them.

How does it work?

Angular triggering change detection

application_ref.ts:

constructor() {
  this._zone.onMicrotaskEmpty.subscribe(
    {next: () => { this._zone.run(() => { this.tick(); }); }});
}

/**
  * Invoke this method to explicitly process change detection
  * and its side-effects.
  * ...
  */
tick(): void {
  // ...
  this._views.forEach((view) => view.detectChanges());
  // ...
}
            

NgZone

Full NgZone API
API Usage
onUnstable EventEmitter that notifies when code begins to execute in the zone.
onMicrotaskEmpty EventEmitter that notifies when microtask queue is empty. May fire more than once.
onStable EventEmitter that notifies when the last onMicrotaskEmpty fires.
run() / runGuarded() Execute code within NgZone. Same as zone.js.
runOutsideAngular() Execute code within parent zone, will not trigger change detection.

Utilize NgZone

  • Long stack trace enabled by default
  • Use APIs to avoid excessive change detection
  • runOutsideAngular() when UI updates are not needed
  • onStable() to wait for group of async code to finish

Angular Demos

Source - ngx-examples/

ChangeDetectionStrategy.OnPush

Less awkward way to do less change detection
  • Set on per component level
  • Will NOT run change detection unless REFERENCE of input changes
  • Will NOT run change detection on children if input hasn't changed
  • Force change detection via ChangeDetectorRef

TL;DR

  • zone.js helps change detection run
  • Hooks exist to know when tasks happen
  • APIs exist to run code that hides from zone.js
  • OnPush detection simple way to get benefits without having to know about zones