What you don't know about zone.js

@JiaLiPassion

Who am I

  • Name: Jia Li
  • Company: Sylabs
  • Zone.js: Code Owner
  • Angular: Trusted Collaborator

Agenda

  • What's Zone.js
  • Zone.js in Angular
  • Some features you may not know

What is Zone.js

A Zone is an execution context that persists across async tasks.

Created by Brian Ford inspired by Dart.

What zone.js can do

  • Provide execution context

  • Provide async task lifecycle hook

  • Provide error handler for async operations

Execution Context

Zone.current.fork({name: 'zone', properties: {key: 'value'}})
   .run(function() {
      setTimeout(function () {
        console.log(Zone.current.name, Zone.current.get('key')); 
        // zone value
      }, 100);
      setTimeout(function() {
        console.log(Zone.current.name, Zone.current.get('key')); 
        // zone value
      }, 200);
      Promise.resolve(1).then(() => {
        console.log(Zone.current.name, Zone.current.get('key')); 
        // zone value
      });
});

LifeCycle Interceptable Hooks

  • onFork
  • onIntercept
  • onInvoke
  • onHandleError
  • onScheduleTask
  • onInvokeTask
  • onCancelTask
  • onHasTask

Implementation: Mokey-Patch

const nativeTimeout = window.setTimeout;
window.setTimeout = function(callback) {
  const zone = Zone.current;
  const wrappedCallback = function() {
     zone.onInvokeTask('setTimeout'); // invoke hook
     return callback.apply(this, arguments);
  }
  zone.onScheduleTask('setTimeout');  // schedule hook
  nativeTimeout.apply(this, wrappedCallback);
}

Async Error Handling

Zone.current.fork(
  {
    name: 'error',
    onHandleError: (delegate, curr, target, error) => {
       logger.error(error);
       return delegate.handleError(target, error);
    }
}).runGuarded(() => {
    setTimeout(() => {
       throw new Error('timeout error');
    });
    setTimeout(() => {
       throw new Error('another timeout error');
    });
});

When should we use Zone

  • Test
    • Sync(Disallow Async)
    • Async(Auto done, Auto cleanup)
    • FakeAsync()
  • Debug
    • LongStackTrace
    • Task Tracing
  • Performance measure
  • Framework AutoRender
  • User Action Tracing
  • Resource Releasing

Zone.js in Angular

NgZone

Change Detection

AsyncTest

FakeAsyncTest

SyncTest

Error Handling

Debug/Tracing

TaskTrackingZone

LongStackTraceZone

Features of zone.js you may not know

Module

Timer

Promise

EventTarget

...

requestAnimationFrame

Disable specified Module

(window as any).__Zone_disable_requestAnimationFrame = true;

Ionic v4

(window as any).__Zone_disable_customElements = true;

Disable Specified Event

// recommended variable name
__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove'];

// deprecated variable name
// __zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove'];

Native Delegate

// ngZone.runOutsideAngular(() => {});

declare let Zone: any;
(window as any)[Zone.__symbol__('setTimeout')](() => {
  // will not in zone.
}, 100);

Promise

ZoneAwarePromise

  • Pass Promise A+ Test
  • Support Promise.prototype.finally
  • Support Bluebird
    • All Bluebird specified APIs will in zone
  • Will support Promise.any and Promise.allSettled

Error Handling

import 'zone.js/dist/zone-error';

Error Handling

import 'zone.js/dist/zone-error';
class MyError extends Error {
}

try {
  throw new MyError('myError');
} catch (error: Error) {
  if (error instanceof MyError) {
    // do some special error handling
  }
}
(window as any).__Zone_Error_BlacklistedStackFrames_policy
   = 'disable'; 

Electron

  • Browser
  • NodeJs
  • Menu
  • Screenshot
  • Shell
  • Main<->Render communication
import 'zone.js/dist/zone-mix';
import 'zone.js/dist/zone-patch-electron';

More APIs Support

  • MediaQuery

  • Notification

  • RTCPeerConnction

  • ShadyDom

  • Cordova

  • ResizeObserver

  • SocketIO

  • UserMedia

  • Jsonp Helper

FakeAsync: Date.now

will support performance api later

it('should advance Date with tick in fakeAsync', 
  fakeAsync(() => {
    const start = Date.now();
    tick(100);
    const end = Date.now();
    expect(end - start).toBe(100);
}));

FakeAsync: jasmine.clock

beforeEach(() => {
      jasmine.clock().install();
});
afterEach(() => {
      jasmine.clock().uninstall();
});
it('should get date diff correctly', () => {
      // automatically run into fake async zone,
      const start = Date.now();
      jasmine.clock().tick(100);
      const end = Date.now();
      expect(end - start).toBe(100);
});

__zone_symbol__fakeAsyncAutoFakeAsyncWhenClockPatched

FakeAsync: rxjs.scheduler

import 'zone.js/dist/zone-patch-rxjs-fake-async';

it('rxjs scheduler should be advanced by tick in fakeAsync', 
  fakeAsync(() => {
        observable.delay(1000).subscribe(v => {
          result = v;
        });
        expect(result).toBeNull();
        testZoneSpec.tick(1000);
        expect(result).toBe('hello');
}));

Better timeout message

it('should timeout', async(() => {
    setTimeout(() => {}, 10000);
    fixture.whenStable(() => {});
}));

Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
--Pendng async tasks are: [type: macroTask, source: setTimeout, args: {handleId:3,isPeriodic:false,delay:10000,args:[]}

Jasmine 3.x/Mocha 5.x

Will support Jest!

  • Better error message
  • Better jest timer(useFakeTimers/runAllTicks/etc...) support

Differential Loading

<script src="zone.js/dist/zone-evergreen.js" 
  type="module"></script>
<script src="zone.js/dist/zone.js" nomodule></script>

Event Bubble Performance

<div (click)="doSomethingElse()">
  <button (click)="doSomething()">click me</button>
</div>
platformBrowserDynamic()
  .bootstrapModule(
  AppModule, 
  {
    ngZoneEventCoalescing: true
  }
)

Solution

Event Listeners

button.addEventListener('click', click1);
button.addEventListener('click', click2);
button.addEventListener('mousedown', mousedown1);

const clickListeners = button.eventListeners('click');
// will get click1 and click2

button.removeAllListeners('click');
// will remove click1 and click2

button.removeAllListeners();
// will remove all listeners for all events

Zone.js is merged into angular mono repo !!!

Thank you!

You don't know about zone.js (white theme)

By jiali

You don't know about zone.js (white theme)

  • 958