iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 9
1
Modern Web

Angular 8 從推坑到放棄系列 第 9

[Day 08]可能是最簡單的 Zone.js 教學

寫在前面,為什麼要寫這一篇

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來解決同步以及非同步來解決這個問題

透過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)包括:

  • onZoneCreated:產生一個新的zone對象時的函數。 zone.fork也會產生一個繼承至基類zone的新zone,形成一個獨立的zone上下文;
  • beforeTask:zone Task執行前的函數;
  • afterTask:zone Task執行完成後的函數;
  • onError:zone運行Task時候的異常函數;

Error Hadling 非同步事件

一般Error Handling 事件

function main () {
    b1.addEventListener('click', bindSecondButton);
}

function bindSecondButton () {
    b2.addEventListener('click', throwError);
}

function throwError () {
    throw new Error('aw shucks');
}

以下是錯誤訊息

Zone.js 處理 Error Handling 事件

看起來沒問題,但是在這段訊息中,我們不知道觸發來源是什麼,實際上是 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的整個過程。

Zone.js 重新編寫的非同步的事件

  • zone.addEventListener;
  • zone.addEventListener、zone.removeEventListener;
  • zone.setTimeout、zone.clearTimeout、zone.setImmediate;
  • zone.setInterval、zone.clearInterval
  • zone.alert;
  • zone.prompt;
  • etc...

為什麼Angular要使用 Zone.js

  • 用來追縱非同步事件
    • Click Event
    • Error Handling
    • Debugging
    • Websockset
    • etc...

參考


上一篇
[Day 07] 基礎 Typescript
下一篇
[Day09] 什麼是 Directives
系列文
Angular 8 從推坑到放棄30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言