What Is Zone.js and How Can I Use It?

Akash Deshmukh
The Startup
Published in
7 min readDec 20, 2020

--

Let me guess, you might be here because either you are tired of seeing the errors in browser’s developer console which we really don’t understand or you are super curious about how exactly the change detection works in angular. Well, I hope after reading this article you’d not only be able to understand the zone errors but also use some awesome features that zone.js provide us.

Let’s begin by understanding how zone.js fiddles slightly with some browser (or I should actually say platform) APIs.

During initialization of any application which imports zone.js, zone.js monkey patches the list of platform APIs.

But what is monkey patching ? Javascript’s highly dynamic nature allows us to change the behaviour of any underlying browser API. We can replace the original API with completely another behaviour or we can simply decorate the existing behaviour with some more behaviour.

Let’s understand with a example:

Here we are storing the existing behaviour of window’s setTimeout in original. On the next line we redefine the behaviour of setTimeout where we can do anything we want and then call the original behaviour which we have stored in original and then return back the result.

During initialization zone.js does the same process along with all the browser APIs.

Let’s take the example of the setTimeout API. During initialization, zone.js creates a ZoneTask using setTimeout’s parameter and schedules it in the current Zone (more on it later). This ZoneTask is an object which implements the Task interface. This ZoneTask is responsible for storing the arguments passed during the invocation of setTimeout, the way of scheduling and invoking the setTimeout (or any other async API).

ZoneTask are classified into three types:

  • MacroTasksetTimeout(), cleatTimeout(), setInterval()……
  • MicroTask — Promise.then()
  • EventTask — element.addEventListener()

ZoneTask has a member named type which can have a value of either microTask, macroTask or eventTask. Structure of ZoneTask is as belows :

Let’s understand with example how our setTimout is converted to ZoneTask. Let’s consider the below example :

Here we have a simple setTimout defined with two arguments, callback function and delay before it’s execution. This task is de-sugared to build the ZoneTask as above.

zone.js schedules this ZoneTask on current Zone, but what is Zone ? Are there multiple time-spaced copies of zones ?

What is Zone ?

A Zone is an execution context that persists across async tasks. In other words, all the async tasks such as setTimeout, Promise, XHRs etc. are executed in a particular context. In case of zone.js, all these async tasks are executed in a single context which is called Zone. But what is execution context ? Please do read this awesome article to understand about the “execution context”.

Zone takes care of scheduling, invoking and keeping track of asynchronous tasks scheduled by application. Zones are named and are responsible for executing the ZoneTasks. During initialisation of an application, zone.js creates a root zone named as ‘<root>’ which pretty much behaves the same as the platform which it runs on (browser in case of frontend applications).

Each zone is an instance of class Zone which provides us with some API which brings us to the next section.

Zone API:

As mentioned above, ZoneTask is executed in the Zone. But how does it happen ? zone.js exposes the API which allows us to use some awesome functionality provided by zone.js. Each defined zone also allows us to define the set of properties in the form of key-value which can be accessed by its members functions.

More details about each API provided by Zone is mentioned below :

So, Let’s play with some of these static APIs exposed by this:

Ok then, Zones are just the execution context where all the ZoneTask’s are executed using API but what can I do with it? Let’s see one by one how it can help us.

1) Forking zones:

Zone also provides us with the ability to create more child zones by exposing one of its API fork allowing us to create hierarchical structure in zones which would help us to have each zone with distinct responsibilities.

Fig 1.

We can create a new Zone using fork API passing the specification for the new zone we want to create. These specifications in terms of code are equivalent to ZoneSpec class.

Let’s try creating the above hierarchy using fork API :

Here, we’re getting reference to the current Zone using Zone.current and then creating a child zone childAZone using fork of current zone where we’re passing an object of ZoneSpec as a parameter. We’re again creating a new child zone of childAZone and are simply logging the names of it’s parent and grandparent zones.

ZoneSpec also includes other multiple members such as onFork, onIntercept, onScheduleTask, OnInvoke etc. But what are those members ? This brings me to the next possible action we can do with zones.

2) Interception:

Zones provide us with the ability to intercept multiple operations done to execute the tasks in the zone. One can avail these using the defining hooks available in ZoneSpec while creating the new Zone.

Detailed explanation of each hook in ZoneSpec is mentioned below :

Let’s try playing around with one of the Hooks:

Here, I am creating the same hierarchy discussed in fig. 1, but while forking new zones, I have passed in one more value in ZoneSpec i.e. onInvoke where I am logging some messages before and after the execution of a function in each zone. Then we’re defining a function helloFunction and executing it in childBZone. This function is then intercepted by childBZone since it defines interception for execution of any function as defined in it’s ZoneSpec while creating it. It prints log “intercepted in childBZone before execution” in the console and then sends this helloFunction to the closest parent which also has defined interception for function executions. The closest parent which also defines interception for functions here is childAZone. So childAZone also intercepts this helloFunction and logs “intercepted in childAZone before execution”. childAZone also follows the same process and sends this function to the closest parent which also defines the interception for any function. Now in this case, there exists no parent of childAZone which has interception defined, so it will simply execute this function. After the execution of this function, all the trailing messages in child zones would be logged in the console. So the final logs in the console is :

Here in each hook, you might have noticed some of the parameters being defined. Let’s go with each parameter one by one :

currentZone — It refers to the Zone which is currently intercepting the task.

targetZone — It refers to the Zone in which the Task was invoked.

task — It refers to the task that’s supposed to be executed.

parentZoneDelegate — It’s a special object ZoneDelegate which maintains the reference to the parent Zone which defines the same hook for interception.

Applications :

Hmmm…. Now that we have learned how zones work, How can it be helpful to me? Let’s see some of the awesome applications.

1) Profiling asynchronous calls :

Have you ever tried finding time taken for the execution of a function which has some asynchronous calls. Let’s try one :

Here we’re trying to log the time taken for the execution for the function sayHelloAfterDelay. We might have expected the output to be some time in milliseconds to be printed after . Well, if we know how event loop works in javascript VM, we can surely come to the conclusion that above code won’t work.

But as we studied, all async tasks are executed in a zone and zone provides us ability to create a new zone and add the interception of these tasks.

You can have a look at this application mentioned in angular source.

2) Decorators :

Interception of the tasks allows us to decorate the execution of tasks with some more functionality.

3) DOM Rendering :

Remember the old days when we used to build websites using VanillaJS. We used to bind the variables using innerHTML on the selected DOM element and update every time the value in script changes. As this change in the bound variables occurs only on execution on asynchronous tasks, we could use zones to intercept these tasks and update the DOM bounding when the execution is completed which is precisely how front-end frameworks like Angular use the zone.js.

--

--