What you don't know about zone.js
@JiaLiPassion
Who am I
- Name: Jia Li
- Company: ThisDot
- Zone.js: Code Owner
- Angular: 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
setTimeout
setTimeout(function() {
console.log('callback invoked');
}, 1000);
setTimeout in Zone
var zone = Zone.current.fork({
name: 'hook',
onScheduleTask: ...,
onInvokeTask: ....,
onHasTask: ...
});
zone.run(() => {
setTimeout(() => {
console.log('timer callback invoked');
}, 100);
});
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
By jiali
You don't know about zone.js
- 2,179