Microtask Queue + RxJS + Angular

Daniel Glejzner - Oct 7 '23 - - Dev Community

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .