Angular 最主要使用 Zone.js 來解決 Angular 非同步 事件的 Error Hadnling 的問題,所以當你看到 Angular 專案某一段出錯時,在 開發者工具的 stack 中會有一堆 Zone.js的紀錄,所以說要學會 Angular的 Debug 技巧之前,最好先知道 Zone.js 的運作方式
Zone.js 提供了一種稱為Zone(區域)的機制,用於封裝和攔截瀏覽器中的非同步活動(例如setTimeout,promise)。
在 web 開發中,我們會遇到非常多非同步的動作,像是某個按鈕的事件、setTimeout 或 Http Request 等等,而 Zone 透過替標準的 Web API 的方式,攔截每個非同步的事件,因此每當非同步事件發生時,都能透過 zone.js 得知。
function f1(){
console.log("F1");
function f2(){
console.log("F2");
}
f1();
f2();
其執行結果:
F1
F2
在這個例子中,因為f1()、f2()是一般的function,因此會循序的由f1執行後,接著在執行f2()。
那假設同步以及非同步的function一起執行呢??
function f1(){
console.log("F1");
function f2(){
console.log("F2");
}
f1(); // Sync
setTimeOut (f2,5000); //Async
其執行結果:
F1
F2
恩...看起來沒問題,F1很順利地執行在F2後面,但問題是我們來看這f1()以及f2()的執行時間總時間總共多少?
我們f1()+f2()的執行時間預計會是5.多秒,但是實際上呢?
function f1(){
console.log("F1");
}
function f2(){
console.log("F2");
}
function MyZone(){
f1(); // Sync
setTimeout(f2,5000); //Async
}
console.time();
MyZone();
console.timeEnd();
其執行結果:
看起來不太妙,理當執行結果應該要是5.x秒,看來time到timeEnd中間,只有F1執行的時間被記錄到。因為Timer只會記錄同步的function。
這時候使用Zone.js來解決同步以及非同步來解決這個問題
function f1() {
console.log("F1");
}
function f2() {
console.log("F2");
}
function MyZone() {
f1(); // Sync
setTimeout(f2, 5000); //Async
}
var z = Zone.current.fork({
onHasTask: function (parent, current, target, hasTask) {
console.timeLog();
}
});
console.time();
z.run(function () {
MyZone();
});
當引用了Zone.js後,建立了一個名為 z 的Zone物件,並且透過fork,建立一個子Zone區域,onHasTask該執行區段所有的function執行完時會執行的結果。
那它到底是怎麼做到的,zone.js採用猴子補丁(Monkey-patched)的暴力方式將JavaScript中的非同步方法都包了一層,使得這些非同步方法都將運行在zone的上下文中。每一個非同步的任務在zone.js都被當做為一個Task,並在Task的基礎上zone.js為開發者提供了執行前後的方法(hook)包括:
function main () {
b1.addEventListener('click', bindSecondButton);
}
function bindSecondButton () {
b2.addEventListener('click', throwError);
}
function throwError () {
throw new Error('aw shucks');
}
以下是錯誤訊息
看起來沒問題,但是在這段訊息中,我們不知道觸發來源是什麼,實際上是 bindSecondButton觸發的並且使透過FristButton繫結的,因為main以及 bindSecondButton 為非同步事件,較難追蹤 實際上報錯的整個過程,我們再把整個main透過zone.js包起來
Zone.current.fork(
{
onHandleError: function (parentZoneDelegate, currentZone, targetZone, error) {
console.log(error.stack);
}
}
).fork(Zone.longStackTraceZoneSpec).run(main);
上述的code是指當這ㄧ段Zone 發生錯誤時,將其Error Stack 列印出來
以下是錯誤訊息:
上述的錯誤訊息可以看出除了原本的Error出錯點以外,包括觸發事件的click以及繫結click的事件都有詳細的紀錄,透過這個方式,可以完整的紀錄Error的整個過程。