@JiaLiPassion
A Zone is an execution context that persists across async tasks.
Created by Brian Ford inspired by Dart.
Provide execution context
Provide async task lifecycle hook
Provide error handler for async operations
execution context is an abstract concept that holds information about the environment within which the current code is being executed
const globalThis = this;
function testFunc() {
console.log('this in testFunc is:', this === globalThis);
}
testFunc();
const testObj = {
testFunc: function() {
console.log('this in testFunc is:', this);
}
};
testObj.testFunc();
const newTestFunc = testObj.testFunc;
newTestFunc();
const newObj = {};
newTestFunc.apply(newObj);
const bindObj = {};
const boundFunc = testObj.testFunc.bind(bindObj);
boundFunc();
function c() {
console.log(new Error('error in c'));
}
function b() {
c.apply({});
}
function a() {
b.call({});
}
a();
// Error: error in c
// at c (js-error.html:4)
// at b (js-error.html:8)
// at a (js-error.html:12)
function c() {
console.log(new Error('error in c'));
}
function timeout() {
setTimeout(c);
}
timeout();
// Error: error in c
// at c (js-error.html:4)
zone.run(function() {
// function is in the zone
// just like `this`, we have a zoneThis === zone
expect(zoneThis).toBe(zone);
setTimeout(function() {
// the callback of async operation
// will also have a zoneThis === zone
// which is the zoneContext when this async operation
// is scheduled.
expect(zoneThis).toBe(zone);
});
Promise.resolve(1).then(function() {
// all async opreations will be in the same zone
// when they are scheduled.
expect(zoneThis).toBe(zone);
});
});
zone.run(function() {
expect(Zone.current).toBe(zone);
setTimeout(function() {
expect(Zone.current).toBe(zone);
});
});
setTimeout(function() {
console.log('callback invoked');
}, 1000);
// Javascirpt 101
function a() {}
function b() {}
function c() {}
function d() {}
a();
setTimeout(c, 100);
setTimeout(d, 100);
b();
// run order
// 1. a
// 2. b
// 3. c
// 4. d
// Javascirpt 101
function a() {// cost 100ms }
function b() {// cost 100ms }
function c() {// cost 100ms }
function d() {// cost 100ms }
performance.start();
a();
setTimeout(c, 100);
setTimeout(d, 200);
b();
performance.end();
Time
a
b
c
d
performance.start()
performance.end()
var zone = Zone.current.fork({
name: 'hook',
onScheduleTask: ...,
onInvokeTask: ....,
onHasTask: ...
});
zone.run(() => {
setTimeout(() => {
console.log('timer callback invoked');
}, 100);
});
zoneA = {
onInvokeTask: (callback) => {
performance.start();
callback();
performance.end();
}
};
zoneA.run(() => {
performance.start();
a();
setTimeout(c);
setTimeout(d);
b();
performance.end();
});
setTimeout(e);
Time
a
b
c
performance.start()
performance.end()
performance.start()
performance.end()
d
performance.start()
performance.end()
e
zoneA
zoneA
zoneA
zoneA
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');
});
});
const api = {
add: function add(a, b) {
return a + b;
}
}
api.add(1, 2); // will return 3;
const originalAdd = api.add;
api.add = function() {
console.log('api.add is called');
return originalAdd.apply(this, arguments);
}
api.add(1, 2); // will log and return 3;
function main () {
b1.addEventListener('click', bindSecondButton);
}
/*
* What if your stack trace could tell you what
* order the user pushed the buttons from the stack trace?
* What if you could log this back to the server?
* Think of how much more productive your debugging could be!
*/
function bindSecondButton () {
b2.addEventListener('click', throwError);
}
function throwError () {
throw new Error('aw shucks');
}
main();
function btnClicked () {
recurRandonGenerateTimeout(10);
}
function recurRandonGenerateTimeout (x) {
if (x > 0) {
setTimeout(function () {
for (var i = x; i < 8; i++) {
recur(x - 1, Math.random()*100);
}
}, t);
}
}
function btnClicked () {
asyncHeavyWork1();
asyncHeavyWork2();
asyncHttpRequest();
}
const fs = require('fs');
fs.open('./test.txt', 'r', (err, fd) => {
if (err) throw err;
fs.fstat(fd, (err, stat) => {
if (err) throw err;
doSomething(fd, err => {});
doSomethingElse(fd, err => {});
});
});
const autoReleaseFSZone = Zone.current.fork({
name: 'releaseFS',
onHasTask: (pd, curr, target, hasTaskState) => {
if (!hasTaskState.macroTask && !hasTaskState.microTask && fd !== -1) {
fs.close(fd, (err) => {
if (err) throw err;
fd = -1;
});
}
return pd.hasTask(target, hasTaskState);
}
});
viewBtnClicked() {
httpRequest(viewUrl);
httpRequest(additionalInfoUrl);
}
orderBtnClicked() {
httpRequest(orderUrl);
httpRequest(transactionUrl);
}
errorBtnClicked() {
throw new Error();
}
function httpBtnClicked() {
httpRequestUrl(viewUrl);
}
function timeoutBtnClicked() {
setTimeout(() => {
data.timeout = 'timeout';
});
}
function addBtnClicked() {
if (!data.num) {
data.num = 0;
}
data.num ++;
}
const api = require('./async-lib');
describe('testAsync', () => {
it('test async operation', (done) => {
let a = 0;
api.testAsync(a, r => {
expect(r).toBe(1);
done();
});
});
});
const api = require('./async-lib');
describe('testAsync', async(() => {
it('test async operation', () => {
let a = 0;
api.testAsync(a, r => {
expect(r).toBe(1);
});
});
}));
describe('testAsync', () => {
it('test long async operation', (done) => {
setTimeout(() => {
// ... expect something.
done();
}, 10000);
});
});
describe('testAsync', () => {
it('test long async operation', fakeAsync(() => {
setTimeout(() => {
// ... expect something.
}, 10000);
tick(10000);
}));
});
NgZone
Tell when to trigger Change Detection
AsyncTest
FakeAsyncTest
SyncTest
Error Handling
Debug/Tracing
TaskTrackingZone
LongStackTraceZone
ngZone.run(() => {
// will be in angular zone
// will trigger change detection
});
ngZone.runOutsideAngular(() => {
// will be outside angular zone
// will not trigger change detection
});