I'm experimenting with zones (zone.js) and I realized I don't know what's the best way to print all the zones from root to the current zone that threw an error.
For example this code uses two nested callbacks with setTimeout()
and then calls a function called failedFunc()
that throws an error:
require('zone.js');
function failedFunc() {
throw new Error("it's broken");
}
let rootZone = Zone.current;
function func1() {
let zoneA = rootZone.fork({name: 'zoneA'});
zoneA.run(() => {
setTimeout(() => func2());
});
}
function func2() {
let zoneB = Zone.current.fork({name: 'zoneB'});
zoneB.run(() => {
setTimeout(() => failedFunc());
});
}
func1();
When I run this example it gives the following output:
/.../zone-test/node_modules/zone.js/dist/zone-node.js:170
throw error;
^
Error: it's broken
at new Error (native)
at failedFunc (/.../zone-test/zone_02.js:12:9) [zoneB]
at Timeout.setTimeout (/.../zone-test/zone_02.js:28:22) [zoneB]
at Zone.runTask (/.../zone-test/node_modules/zone.js/dist/zone-node.js:166:47) [<root> => zoneB]
at Timeout.ZoneTask.invoke (/.../zone-test/node_modules/zone.js/dist/zone-node.js:416:38) [<root>]
at Timeout.data.args.(anonymous function) [as _onTimeout] (/.../zone-test/node_modules/zone.js/dist/zone-node.js:1590:25) [<root>]
at ontimeout (timers.js:365:14) [<root>]
at tryOnTimeout (timers.js:237:5) [<root>]
at Timer.listOnTimeout (timers.js:207:5) [<root>]
The correct path for the zone that threw the error is <root>
=> zoneA
=> zoneB
.
However it's not obvious from the output. There's just [root]
and zoneB
and it doesn't mention the zoneA
which is confusing. I guess this is because the zone.js
's monkey patch only adds zone info to the lines in the call stack. So when I'm using setTimeout()
than creating zoneA
doesn't correspond to any line in the output and that's why I don't see it anywhere.
Nonetheless, I can print the path to the current zone by iterating all it's parents but this means I need to know where the error first happened and add the following code to it (which would be very tedious in practice):
function failedFunc() {
let zone = Zone.current;
let zones = [];
while (zone) {
zones.push(zone.name);
zone = zone.parent;
}
console.log(zones);
throw new Error("it's broken");
}
// ...
Now when I run this I'll get what I need:
[ 'zoneB', 'zoneA', '<root>' ]
/.../zone-test/node_modules/zone.js/dist/zone-node.js:170
throw error;
^
Error: it's broken
at new Error (native)
So I'm wondering how to use zone.js in practice and in a way that it's easier than this.
js is a way of creating a new Context and getting the code executed within that context/Zone. Any code enclosed within a Zone whether synchronous or asynchronous is executed within the same context. Zone. js is basically used to create new context and execute code within it.
In Angular, Zone. js is used to detect when certain async operations occur to trigger a change detection cycle. In this post, we will be looking in-depth on how Zone. js is used by Angular for dirty checking and running UI updates.
js is the key to Angular's change detection. Zone creates a wrapper around all asynchronous operations in the browser such as user interactions, HTTP, timers and any other changes that can cause changes in state. Zone knows when any these operations completes.
A “zone” is a way to persist execution context across asynchronous tasks. Angular uses zones to enable automatic change detection in response to changes made by asynchronous tasks.
To obtain the full stacktrace of your zone
you have to use long-stack-trace-zone
Please note, that I'm throwing an exception, this is why you will see an error in my snippet. The question and the answer is about printing full stacktrace:
function failedFunc() {
throw new Error("it's broken");
}
function func1() {
let zoneA = Zone.current.fork({
name: 'zoneA'
});
zoneA.run(function() {
setTimeout(function() {
func2()
});
});
}
function func2() {
let zoneB = Zone.current.fork({
name: 'zoneB'
});
zoneB.run(function() {
setTimeout(function() {
failedFunc()
});
});
}
Zone.current.fork({
onHandleError: function(parentZoneDelegate, currentZone, targetZone, error) {
console.log(error.stack);
}
}).fork(Zone.longStackTraceZoneSpec).run(func1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.8.5/zone.min.js"></script>
<!-- https://raw.githubusercontent.com/angular/zone.js/master/dist/long-stack-trace-zone.min.js -->
<script>
!function(t,a){"object"==typeof exports&&"undefined"!=typeof module?a():"function"==typeof define&&define.amd?define(a):a()}(this,function(){"use strict";function t(){return new Error(u)}function a(){try{throw t()}catch(a){return a}}function e(t){return t.stack?t.stack.split(i):[]}function n(t,a){for(var n=e(a),r=0;r<n.length;r++){var c=n[r];s.hasOwnProperty(c)||t.push(n[r])}}function r(t,a){var e=[a.trim()];if(t)for(var r=(new Date).getTime(),c=0;c<t.length;c++){var o=t[c],s=o.timestamp,f="____________________Elapsed "+(r-s.getTime())+" ms; At: "+s;f=f.replace(/[^\w\d]/g,"_"),e.push(h.replace(_,f)),n(e,o.error),r=s.getTime()}return e.join(i)}function c(t,a){a>0&&(t.push(e((new l).error)),c(t,a-1))}function o(){var t=[];c(t,2);for(var a=t[0],e=t[1],n=0;n<a.length;n++){var r=a[n];if(r.indexOf(u)==-1){var o=r.match(/^\s*at\s+/);if(o){h=o[0]+_+" (http://localhost)";break}}}for(var n=0;n<a.length;n++){var r=a[n],i=e[n];if(r!==i)break;s[r]=!0}}var i="\n",s={},f="__creationTrace__",u="STACKTRACE TRACKING",_="__SEP_TAG__",h=_+"@[native]",l=function(){function t(){this.error=g(),this.timestamp=new Date}return t}(),d=t(),k=a(),g=d.stack?t:k.stack?a:t;Zone.longStackTraceZoneSpec={name:"long-stack-trace",longStackTraceLimit:10,getLongStackTrace:function(t){if(t){var a=t[Zone.__symbol__("currentTask")],e=a&&a.data&&a.data[f];return e?r(e,t.stack):t.stack}},onScheduleTask:function(t,a,e,n){var r=Zone.currentTask,c=r&&r.data&&r.data[f]||[];return c=[new l].concat(c),c.length>this.longStackTraceLimit&&(c.length=this.longStackTraceLimit),n.data||(n.data={}),n.data[f]=c,t.scheduleTask(e,n)},onHandleError:function(t,a,e,n){var c=Zone.currentTask||n.task;if(n instanceof Error&&c){var o=r(c.data&&c.data[f],n.stack);try{n.stack=n.longStack=o}catch(i){}}return t.handleError(e,n)}},o()});
</script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With