Angular zone JS introduction

Maybe you've heard that Angular uses zone JS, but why should Angular use zone JS, what functions can it provide? Let's write a separate article today JS, about its role in the Angular framework will be described in the next article.

What is Zone? The official document explains this: Zone is an execution context that spans multiple asynchronous tasks. In a word, Zone has a particularly powerful ability to intercept or track asynchronous tasks. Next, we will show its ability through an example and briefly analyze the working principle behind it.

<button id="b1">Bind Error</button>
<button id="b2">Cause Error</button>
<script>
  function main() {
    b1.addEventListener('click', bindSecondButton);
  }
  function bindSecondButton() {
    b2.addEventListener('click', throwError);
  }
  function throwError() {
    throw new Error('aw shucks');
  }
  main();
</script>

This is a simple HTML page. When the page is loaded, a click event will be added to the first button. The function of the click event function is to add a click event to the second button, while the function of the click event function of the second button is to throw an exception. We click the first button and the second button in turn, and the console displays as follows:

((index):26 Uncaught Error: aw shucks
    at HTMLButtonElement.throwError (((index):26:13)

But if we pass zone JS starts and runs the code. What is the difference in console output? Let's adjust the startup code first:

 Zone.current.fork(
      {
        name: 'error',
        onHandleError: function (parentZoneDelegate, currentZone, targetZone, error) {
          console.log(error.stack);
        }
      }
    ).fork(Zone.longStackTraceZoneSpec).run(main);

At this time, the console output is as follows:

Error: aw shucks
    at HTMLButtonElement.throwError (((index):26:13)
    at ZoneDelegate.invokeTask (zone.js:406:31)
    at Zone.runTask (zone.js:178:47)
    at ZoneTask.invokeTask [as invoke] (zone.js:487:34)
    at invokeTask (zone.js:1600:14)
    at HTMLButtonElement.globalZoneAwareCallback (zone.js:1626:17)
    at ____________________Elapsed_571_ms__At__Mon_Jan_31_2022_20_09_09_GMT_0800_________ (localhost)
    at Object.onScheduleTask (long-stack-trace-zone.js:105:22)
    at ZoneDelegate.scheduleTask (zone.js:386:51)
    at Zone.scheduleTask (zone.js:221:43)
    at Zone.scheduleEventTask (zone.js:247:25)
    at HTMLButtonElement.addEventListener (zone.js:1907:35)
    at HTMLButtonElement.bindSecondButton (((index):23:10)
    at ZoneDelegate.invokeTask (zone.js:406:31)
    at Zone.runTask (zone.js:178:47)
    at ____________________Elapsed_2508_ms__At__Mon_Jan_31_2022_20_09_06_GMT_0800_________ (localhost)
    at Object.onScheduleTask (long-stack-trace-zone.js:105:22)
    at ZoneDelegate.scheduleTask (zone.js:386:51)
    at Zone.scheduleTask (zone.js:221:43)
    at Zone.scheduleEventTask (zone.js:247:25)
    at HTMLButtonElement.addEventListener (zone.js:1907:35)
    at main (((index):20:10)
    at ZoneDelegate.invoke (zone.js:372:26)
    at Zone.run (zone.js:134:43)

Through comparison, we know: do not introduce zone JS, we can only know through the error call stack that the exception is thrown by the click function of button 2. And introduced zone After JS, we not only know that the exception is thrown by the click function of button 2, but also know that its click function is bound by the click function of button 1. We can even know that the initial application startup is triggered by the main function. This ability to continuously track multiple asynchronous tasks is extremely important in large and complex projects. Now let's look at zone JS is how to do it.

zone.js takes over the asynchronous API s provided by the browser, such as click events, timers and so on. It is precisely because of this that it can have stronger control intervention ability for asynchronous operation and provide more capabilities. Now let's take the click event as an example to see how it does it.

proto[ADD_EVENT_LISTENER] = makeAddListener(nativeAddEventListener,..)

In the above code, proto refers to EventTarget Prototype, that is, this line of code redefines the addEventListener function. Let's continue to see what the makeAddListener function does.

function makeAddListener() {
  ......
  // Key code 1
  nativeListener.apply(this, arguments);
  ......
  // Key code 2
  const task = zone.scheduleEventTask(source, ...)
  ......
}

This function mainly does two things: one is to execute the addEventListener function provided by the browser in the user-defined function, and the other is to arrange an event task for each click function, which is also zone JS is an important factor in the strong intervention ability of asynchronous API.

Now let's go back to the example at the beginning of this article to see why the console can output a complete function call stack. We just analyzed the makeAddListener function, which mentioned that it arranges an event task for each click function, that is, zone Execution of the scheduleeventtask function. The scheduling event task function ultimately executes onScheduleTask:

onScheduleTask: function (..., task) {
  const currentTask = Zone.currentTask;
  let trace = currentTask && currentTask.data && currentTask.data[creationTrace] || [];
  trace = [new LongStackTrace()].concat(trace);
  task.data[creationTrace] = trace;
}

The complete function call stack output from the console at the beginning of the article is stored in currenttask In data [creationtrace], it is an array composed of LongStackTrace instances. Every time an asynchronous task occurs, the onScheduleTask function records the current function call stack storage. We can see from the constructor of class LongStackTrace:

class LongStackTrace {
    constructor() {
        this.error = getStacktrace();
        this.timestamp = new Date();
    }
}
function getStacktraceWithUncaughtError() {
    return new Error(ERROR_TAG);
}

this.error stores the function call stack. The getStacktrace function usually calls the getStacktraceWithUncaughtError function. When we see the new Error, we can probably know how to get the whole call stack.

This article only analyzes zone JS capability is an example. If you want to know more functions, you can refer to the official documentation. Through this example, I hope readers can understand zone JS has a general understanding, because it is also an indispensable cornerstone of Angular change detection. I will explain this in the next article. Welcome to my personal WeChat official account, Zhu Yujie blog. The follow-up will bring more front-end knowledge sharing.

Keywords: Web Development angular

Added by MA06 on Thu, 03 Feb 2022 04:49:22 +0200