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)
- 952