DEV Community

Cover image for Microtask Queue + RxJS + Angular
Daniel Glejzner for This is Angular

Posted on

Microtask Queue + RxJS + Angular

The What, When, and Why

When working with Angular, you can experience complex mechanisms that are not initially visible. When understood, can significantly enhance your application’s performance.

Learn about interplay between the Microtask Queue, RxJS, and Angular’s change detection mechanism.

Image source: github.com/TheAlgorithms/JavaScript

Microtask vs. Macrotask

Before diving deep, it’s essential to differentiate between microtasks and macrotasks. Observables, a core part of RxJS, are microtasks in async scenario.

This ensures they execute quicker than macrotasks, such as setTimeout. But why does this matter?

RxJS and Asynchronicity

Observable values (async scenario) in RxJS are processed after promises. This sequence affects the execution order, which can be crucial when dealing with asynchronous operations.

Angular’s Change Detection

Angular’s change detection mechanism is designed to run after the microtask queue is emptied.

This ensures that the view updates only after all the microtasks, including observables, have been processed.

It’s a subtle yet powerful design choice that ensures consistency in the UI.

The Role of Zone.js

Zone.js plays a significant role. It monitors asynchronous operations and ensures that Angular’s change detection is triggered post the completion of microtasks.

RootZone (outerZone)

At the foundation of Zone.js is the root zone. This is the top-level zone that provides the basic mechanisms to track asynchronous operations.

Forking and Hierarchical Structure

Zones in Zone.js are hierarchical. This means that new zones are created by "forking" an existing zone. When you fork a zone, the new zone inherits the behaviours of its parent but can also have additional behaviours or modifications.

This hierarchical structure allows for a cascading system where child zones can benefit from the tracking mechanisms of their parent zones while also introducing specific behaviours.

NgZone (innerZone)

In the context of Angular, the framework doesn’t directly use the root zone. Instead, Angular creates its specialized zone by forking the root zone.

This forked zone, known as NgZone or sometimes referred to as the "inner zone," is augmented with Angular-specific behaviors. It's within this NgZone that Angular tracks changes and decides when to run its change detection.

The ability to fork zones and create this hierarchical structure is powerful. It allows for modular and layered tracking of asynchronous operations.

For instance, while the root zone might provide basic tracking, child zones (like NgZone) can introduce application-specific behaviors without affecting the broader tracking mechanisms.

In Angular’s case, the NgZone ensures that the framework can efficiently track changes and update the UI, building upon the foundational capabilities of the root zone.

A Typical Sequence

Let’s break down a typical sequence of events:

  1. A user interacts, perhaps with a button click.

  2. This interaction triggers an event.

  3. An API call is made in response.

  4. Promises and Observables (async scenario) are pushed into the Microtask Queue.

  5. Functions scheduled with setTimeout go into the Macrotask Queue.

  6. Within the Microtask Queue, promises resolve first.

  7. Observables then emit their values.

  8. Data is processed using RxJS operators.

  9. Angular checks for changes in the data.

  10. The UI is updated with fresh data.

  11. Finally, functions in the Macrotask Queue, like those scheduled with setTimeout, execute.

Not that hard huh?

The interplay between the Microtask Queue, RxJS, and Angular is a complex work of internal mechanisms.

But understanding this is invaluable. It not only aids in performance optimizations but also helps in resolving potential race conditions.

These nuances can make the difference between a good application and a great one.

Top comments (1)

Collapse
 
felikf profile image
felikf • Edited

Hi Daniel, thanks for a great article. I found them always very interesting and inspiring :) .
There is one thing that I do not understand in depth:

First question

Observables, a core part of RxJS, are microtasks in async scenario.

Can we in general say that all observables are microtasks?

How about this snippet:

// macro
of('observable macro').pipe(delay(0)).subscribe(v => console.log(v))

// micro
Promise.resolve('promise micro').then(v => console.log(v))
Enter fullscreen mode Exit fullscreen mode

The observable in this example is async macrotask, isn't it?

Second question

The second think that I would like to understand is when Angular starts change detection for changes initiated as macrotask (setTimeout).

How does the NgZone notifies Angular to update the view?
As far as I know it only starts the change detection when the microtask is empty.


Also for dirty marked components, what is the trigger that runns the change detection if there are not microtask in the queue? :)