Zone.js Internals
@JiaLiPassion
Who am I
- Name: Jia Li
- Company: ThisDot
- Zone.js: Code Owner
- Angular: Collaborator
Agenda
- Zone.js internal implementation
- Zone.js in Angular
- Some flags in zone.js will help you in angular
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 interceptable hook
-
Provide async error handler
Execution Context across Async operations
Zone.current.fork({name: 'zone', properties: {key: 'value'}})
.run(function() {
// the callback of async operations will remember the context
// when it is scheduled
console.log(Zone.current.name, Zone.current.get('key'));
// zone value
setTimeout(function () {
console.log(Zone.current.name, Zone.current.get('key'));
// zone value
}, 100);
const p = Promise.resolve(1);
p.then(() => {
console.log(Zone.current.name, Zone.current.get('key'));
// zone value
});
});
Point 1: Zone.current
zoneA.run(() => {
const currentZoneA = Zone.current; // will be zoneA
zoneB.run(() => {
const currentZoneB = Zone.current; // will be zoneB
});
});
Point 2: root zone
const rootZone = Zone.current; // will be <root> zone
console.log(`root zone's name is: ${rootZone.name}`);
// will be <root>
Point 3: ZoneSpec
const rootZone = Zone.current; // will be <root> zone
console.log(`root zone's name is: ${rootZone.name}`);
// will be <root>
LifeCycle Interceptable Hooks
- onFork
- onIntercept
- onInvoke
- onHandleError
- onScheduleTask
- onInvokeTask
- onCancelTask
- onHasTask
setTimeout
zone.run(() => {
// Here the onScheduleTask will be invoked
// And onHasTask will also be invoked
setTimeout(function() {
// Here the onInvokeTask will be invoked
console.log('callback invoked');
} // Here the onHasTask will be invoked again,
1000);
});
setTimeout in Zone
var zone = Zone.current.fork({
name: 'hook',
onScheduleTask: ...,
onInvokeTask: ....,
onHasTask: ...
});
zone.run(() => {
setTimeout(() => {
console.log('timer callback invoked');
}, 100);
});
Zone.js Event loop Demo
https://zone-ebb17.firebaseapp.com/
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');
});
});
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);
}
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
Change Detection
AsyncTest
FakeAsyncTest
SyncTest
Error Handling
Debug/Tracing
Zone.js in Angular
DOM API
setTimeout
Promise
EventTarget
...
Zone.js
Monkey-Patch
Angular
What's new
- Performance
- Promise
- Error handling
- Electron
- More APIs support
- Test
- Angular Elements (Proposal)
Performance
- Modularization
- Event
- Native Delegate
Modularization
Timer
Promise
EventTarget
requestAnimationFrame
...
Notification
MediaQuery
UserMedia
...
requestAnimationFrame
__Zone_disable_requestAnimationFrame=true
Event
__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove'];
@HostListener('window:mousemove.nozone')
mouseMoveListener() {
// will not in ngZone.
}
<div (scroll.nozone)="scrollHandler();"></div>
Proposal?
Native Delegate
declare let Zone: any;
(window as any)[Zone.__symbol__('setTimeout')](() => {
// will not in zone.
}, 100);
Promise
- Pass Promise A+ Test
- Support Promise.prototype.finally
- Support Bluebird
- All Bluebird specified APIs will in zone
- No more 'Zone.js has detected that ZoneAwarePromise `(window|global).Promise` has been overwritten ' error.
- No more 'Zone already loaded' error. (proposal)
import 'zone.js/dist/zone';
import 'core-js/promise';
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'; // 'default', 'lazy'
Electron
import 'zone.js/dist/zone-mix';
import 'zone.js/dist/zone-patch-electron';
- Browser
- NodeJs
- Menu
- Screenshot
- Shell
- Main<->Render communication
More APIs Support
-
MediaQuery
-
Notification
-
RTCPeerConnction
-
ShadyDom
-
Cordova
-
ResizeObserver
-
SocketIO
-
UserMedia
-
Jsonp Helper
Test
- Bundle
- FakeAsync enhancement
- Async enhancement
- Jasmine/Mocha
- Unified Test Lib
Zone.js can be a standalone test library.
Test Bundle
import 'zone.js/dist/long-stack-trace';
import 'zone.js/dist/proxy';
import 'zone.js/dist/sync-test';
import 'zone.js/dist/jasmine-patch';
import 'zone.js/dist/async-test';
import 'zone.js/dist/fake-async';
import 'zone.js/dist/promise-testing';
import 'zone.js/dist/zone-testing';
Test Bundle(Proposal)
import 'zone.js/dist/zone-testing';
describe('zone-testing', () => {
it('fakeAsync', fakeAsyncTest(() => {
tick(100);
discardPeriodicTasks();
flushMicrotasks();
});
it ('async', asyncTest(() => {}));
});
FakeAsync: Date.now
it('should advance Date when 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', () => { // we don't need fakeAsync here.
// automatically run into fake async zone, because jasmine.clock() is installed.
const start = Date.now();
jasmine.clock().tick(100);
const end = Date.now();
expect(end - start).toBe(100);
});
__zone_symbol__fakeAsyncPatchLock
FakeAsync: rxjs.scheduler
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
Angular Elements (Scoped Zone Proposal)
Web App
JQuery
React
Angular Elements
Zone.js
Zone.js
Zone.js
Update recently for Angular Elements
- Custom Element V1 Support
- Support loading Zone.js multiple times
- Optimized root zone
-
rxjs 6/typescript 3
Thank you!
Miško
Takeshi Kondo
Thank You!
- https://github.com/angular/zone.js
- @JiaLiPassion
Zone.js Internals
By jiali
Zone.js Internals
- 865