我正在尝试区域(
zone.js),我意识到我不知道什么是打印从根到当前区域的所有区域抛出错误的最佳方法.
例如,此代码使用setTimeout()的两个嵌套回调,然后调用一个名为failFunc()的函数,该函数会引发错误:
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();
当我运行这个例子它给出以下输出:
/.../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>]
抛出错误的区域的正确路径为< root> => zoneA => zoneB中.
但是从输出来看并不明显.只有[root]和zoneB,它没有提到那个令人困惑的zoneA.我猜这是因为zone.js的猴子补丁只会添加区域信息到调用堆栈中的行.所以当我使用setTimeout()而不是创建zoneA不对应于输出中的任何行,这就是为什么我没有看到它在任何地方.
尽管如此,我可以通过迭代所有的父母来打印当前区域的路径,但这意味着我需要知道错误在哪里发生,并添加以下代码(这在实践中将是非常繁琐的):
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"); } // ...
现在,当我运行这个我会得到我需要的:
[ 'zoneB','zoneA','<root>' ] /.../zone-test/node_modules/zone.js/dist/zone-node.js:170 throw error; ^ Error: it's broken at new Error (native)
所以我想知道如何在实践中使用zone.js,并以一种比这更容易的方式.
解决方法
要获得您所在区域的完整堆栈跟踪,您必须使用
long-stack-trace-zone
请注意,我抛出异常,这就是为什么您会在我的代码段中看到错误.问题和答案是打印完整的堆栈跟踪:
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,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>