---
{
title: "Angular Internals: How Reactivity Works with Zone.js",
description: "Reactivity is core to JavaScript frameworks; changing data should cause a re-render. How does this work in Angular? Let's dive into the Angular source code to see.",
published: '2023-01-02T13:45:00.284Z',
authors: ['crutchcorn'],
tags: ['angular', 'javascript'],
attached: [],
license: 'cc-by-nc-sa-4'
}
---
> This article is an advanced look at how Angular works under-the-hood. The contents within may not be clear if you're not already fairly familiar with JavaScript. If you want to learn how to _use_ Angular and haven't before, look at [my book "The Framework Field Guide", which teaches React, Angular, and Vue from scratch](https://framework.guide) instead.
If you've been following the JavaScript framework ecosystem, you may have heard the term "Reactivity" lately; they've been a hot commodity to talk about, from [SolidJS' "fine-grained" reactivity](https://dev.to/ryansolid/a-hands-on-introduction-to-fine-grained-reactivity-3ndf) to [Preact adding in a reactive primitive with the name of "Signals"](https://preactjs.com/guide/v10/signals/).
The concept of reactivity, at least at first glance, is a straightforward one: When you change a bit of code _here_, it updates a bit of code _there_ automatically. This is commonplace within front-end frameworks, where it's imperative to re-render updated content when you update the data stored in JavaScript.
During discussions of reactivity and front-end frameworks, one "odd duck" stands out as a vastly different implementation from the others: Angular.
Take the following button counter reactivity example in each framework:
# Angular
```typescript
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
`,
})
export class AppComponent {
count = 0;
addOne() {
this.count++;
}
}
```
# React
```jsx
const App = () => {
const [count, setCount] = useState(0);
const addOne = () => setCount(count+1);
return ;
}
```
# Vue
```vue
```
In this example, we can see that React uses explicit update calls (`setX`) to track when the state changes, and Vue uses a proxy and a special property name (`.value`) to seemingly magically track state.
But what about Angular?
Angular just mutates the `count` variable, and the framework seems to count the state changes. How does that work under-the-hood? What mechanism is being used to tell the template to re-render?
The short answer is that Angular uses something called "Zone.js" to track all asynchronous APIs via a series of polyfills, and uses those Zones to re-render "dirty" content in Angular's tree.
> What does any of that mean? That's a lot of words that don't seem to mean very much if you're not already in the know.
I agree; let's answer this better with a longer step-by-step explanation of how Angular does its rendering and reactivity using Zone.js.
This step-by-step explanation will have us explore:
- [How Angular's template compiler outputs functions that render contents](#template-compiler)
- [How Angular's templates are called in order to update contents on-screen](#create-template-function-call)
- [How Angular detects user changes and re-renders screen contents automatically](#refresh-view)
- [What Zone.js is and how to use it with and without Angular](#zone-basics)
- [How Angular has its own internal instance of Zone.js called NgZone](#ng-zone)
- [How Angular's change detection would work without Zone.js (and why it's a DX nightmare)](#disable-ng-zone)
- [How Zone.js Monkey-patches async APIs to call change detection](#zone-patch-intro)
# How Angular's template compiler works {#template-compiler}
Earlier last year, the Angular team published a blog post titled ["How the Angular Compiler Works"](https://blog.angular.io/how-the-angular-compiler-works-42111f9d2549). In it, they demonstrated how the `NGC` compiler takes the following code:
```typescript
import {Component} from '@angular/core';
@Component({
selector: 'app-cmp',
template: 'Your name is {{name}}',
})
export class AppCmp {
name = 'Alex';
}
```
And outputs something like this:
```javascript
import { Component } from '@angular/core';
import * as i0 from "@angular/core";
export class AppCmp {
constructor() {
this.name = 'Alex';
}
}
AppCmp.ɵfac = function AppCmp_Factory(t) { return new (t || AppCmp)(); };
AppCmp.ɵcmp = i0.ɵɵdefineComponent({
type: AppCmp,
selectors: [["app-cmp"]],
decls: 2,
vars: 1,
template: function AppCmp_Template(rf, ctx) {
if (rf & 1) {
i0.ɵɵelementStart(0, "span");
i0.ɵɵtext(1);
i0.ɵɵelementEnd();
}
if (rf & 2) {
i0.ɵɵadvance(1);
i0.ɵɵtextInterpolate1("Your name is ", ctx.name, "");
}
},
encapsulation: 2
});
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AppCmp, [{
type: Component,
args: [{
selector: 'app-cmp',
template: 'Your name is {{name}}',
}]
}], null, null); })();
```
While their article goes more in-depth into how the compiler works (it's a great read!), let's keep our focus narrow for the intention of this article.
Namely, let's look at the `template` property function on the `ɵɵdefineComponent` function call.
```javascript
template: function AppCmp_Template(rf, ctx) {
if (rf & 1) {
i0.ɵɵelementStart(0, "span");
i0.ɵɵtext(1);
i0.ɵɵelementEnd();
}
if (rf & 2) {
i0.ɵɵadvance(1);
i0.ɵɵtextInterpolate1("Your name is ", ctx.name, "");
}
}
```
Here, we're receiving two arguments: `rf` (short for "render flags") and `ctx` (short for "context"). This function is called by Angular itself when the template is ready to either:
1) Be rendered for the first time.
2) Have its contents updated afterward.
Depending on how the template needs to be re-run, the "render flag" (`rf`) will be passed differently, allowing Angular more control over how code is updated.
There are [only two flags that are currently defined in Angular 15](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/interfaces/definition.ts#LL50-L56C2):
```typescript
// Source code from @angular/core
// angular/packages/core/src/render3/interfaces/definition.ts
export const enum RenderFlags {
/* Whether to run the creation block (e.g. create elements and directives) */
Create = 0b01,
/* Whether to run the update block (e.g. refresh bindings) */
Update = 0b10
}
```
The first render flag that will be passed to the `template` function is the `Create` flag, which will call the first `if` statement. Let's strip away everything but the first `if` statement:
```javascript
i0.ɵɵelementStart(0, "span");
i0.ɵɵtext(1);
i0.ɵɵelementEnd();
```
Here, very generally, Angular is saying: "create a `span` element, and mark it such that text should be placed within it".
After this is run, Angular runs the `Update` render flag through the template compiler:
```javascript
i0.ɵɵadvance(1);
i0.ɵɵtextInterpolate1("Your name is ", ctx.name, "");
```
Here, it's saying that we should interpolate the string `"Your name is Alex"` based on the property received from `ctx.name` and place it into the element's text area.
By having our template function contain two distinct render phases - triggered by flags passed into the function - we're able to create the `span` on the first render and update the text values of the `span` on subsequent renders without the need for re-initializing the `span` element each time we change the element's text.
## Exactly how is the template compiler run _by Angular_? {#create-template-function-call}
As mentioned previously, Angular calls this render function with two different render flags: `Create` and `Update`.
But don't take my word for it! Let's take a look at Angular's source code.
[Defined in `@angular/core` is a function called `renderComponent`](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/instructions/shared.ts#L1663-L1669):
```typescript
// Angular 15 source code
// angular/packages/core/src/render3/instructions/shared.ts
function renderComponent(hostLView: LView, componentHostIdx: number) {
ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode');
const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
const componentTView = componentView[TVIEW];
syncViewWithBlueprint(componentTView, componentView);
renderView(componentTView, componentView, componentView[CONTEXT]);
}
```
This function, very generally, accesses a component's `View` ([a concept I've written about before, core to Angular's internal reference to HTML elements](/posts/angular-templates-start-to-source#View-Containers)) and renders it using Angular's `renderView` function.
[Let's look in said `renderView` function](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/instructions/shared.ts#LL286-L300C6):
```typescript
// Angular 15 source code
// angular/packages/core/src/render3/instructions/shared.ts
export function renderView(tView: TView, lView: LView, context: T): void {
ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
enterView(lView);
try {
const viewQuery = tView.viewQuery;
if (viewQuery !== null) {
executeViewQueryFn(RenderFlags.Create, viewQuery, context);
}
// Execute a template associated with this view, if it exists. A template function might not be
// defined for the root component views.
const templateFn = tView.template;
if (templateFn !== null) {
executeTemplate(tView, lView, templateFn, RenderFlags.Create, context);
}
// ...
}
```
Here, we can see the `executeTemplate` function being called with the `RenderFlags.Create` flag, just like we outlined before.
There's no special magic happening [inside of the `executeTemplate` function](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/instructions/shared.ts#L480), either. In fact, this is the whole thing:
```typescript
// Angular 15 source code
// angular/packages/core/src/render3/instructions/shared.ts
function executeTemplate(
tView: TView, lView: LView, templateFn: ComponentTemplate, rf: RenderFlags, context: T) {
const prevSelectedIndex = getSelectedIndex();
const isUpdatePhase = rf & RenderFlags.Update;
try {
setSelectedIndex(-1);
if (isUpdatePhase && lView.length > HEADER_OFFSET) {
// When we're updating, inherently select 0 so we don't
// have to generate that instruction for most update blocks.
selectIndexInternal(tView, lView, HEADER_OFFSET, !!ngDevMode && isInCheckNoChangesMode());
}
const preHookType =
isUpdatePhase ? ProfilerEvent.TemplateUpdateStart : ProfilerEvent.TemplateCreateStart;
profiler(preHookType, context as unknown as {});
templateFn(rf, context);
} finally {
setSelectedIndex(prevSelectedIndex);
const postHookType =
isUpdatePhase ? ProfilerEvent.TemplateUpdateEnd : ProfilerEvent.TemplateCreateEnd;
profiler(postHookType, context as unknown as {});
}
}
```
If we simplify this function a bit to narrow our focus, we're left with:
```typescript
// Simplified Angular 15 source code
// angular/packages/core/src/render3/instructions/shared.ts
function executeTemplate(
tView: TView, lView: LView, templateFn: ComponentTemplate, rf: RenderFlags, context: T) {
// ...
templateFn(rf, context);
// ...
}
```
Here, in this narrowed focus, we can see that when we execute:
```typescript
// Simplified Angular 15 source code
// angular/packages/core/src/render3/instructions/shared.ts
const templateFn = tView.template;
// ...
executeTemplate(tView, lView, templateFn, RenderFlags.Create, context);
```
We're simply calling the component's `template` function with a `RenderFlags.Create` argument as well as the function's `context`.
## What about when the component updates? {#update-template-function-call}
Just as there is a clear demonstration of when the component's `template` function is called with `RenderFlags.Create`, there's also a pretty cut-and-dry example of the `template` function being called with `RenderFlags.Update`.
This `Update` flag is passed by [Angular's `refreshView` function](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/instructions/shared.ts#L354), which is called by Angular when a component is ready to update.
```typescript
// Angular 15 source code
// angular/packages/core/src/render3/instructions/shared.ts
export function refreshView(
tView: TView, lView: LView, templateFn: ComponentTemplate<{}>|null, context: T) {
ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
const flags = lView[FLAGS];
if ((flags & LViewFlags.Destroyed) === LViewFlags.Destroyed) return;
enterView(lView);
// Check no changes mode is a dev only mode used to verify that bindings have not changed
// since they were assigned. We do not want to execute lifecycle hooks in that mode.
const isInCheckNoChangesPass = ngDevMode && isInCheckNoChangesMode();
try {
resetPreOrderHookFlags(lView);
setBindingIndex(tView.bindingStartIndex);
if (templateFn !== null) {
executeTemplate(tView, lView, templateFn, RenderFlags.Update, context);
}
// ...
}
```
That last line should look pretty familiar; the `executeTemplate` shows up again and is passed `RenderFlags.Update` this time!
While this is pretty neat to see so plainly, it leaves an important question out in the open: How _does_ the component know when it's ready to update?
# Inside Angular's change detection; when `refreshView` is called {#refresh-view}
To answer the question of "how does Angular know when a component is ready to update", let's follow the stack trace of when the `refreshView` function is called.
If we take a step one level up, [we can see that `refreshView` is called within a function called `detectChangesInternal`](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/instructions/shared.ts#L1770):
```typescript
// Angular 15 source code
// angular/packages/core/src/render3/instructions/shared.ts
export function detectChangesInternal(
tView: TView, lView: LView, context: T, notifyErrorHandler = true) {
const rendererFactory = lView[RENDERER_FACTORY];
// Check no changes mode is a dev only mode used to verify that bindings have not changed
// since they were assigned. We do not want to invoke renderer factory functions in that mode
// to avoid any possible side-effects.
const checkNoChangesMode = !!ngDevMode && isInCheckNoChangesMode();
if (!checkNoChangesMode && rendererFactory.begin) rendererFactory.begin();
try {
refreshView(tView, lView, tView.template, context);
} catch (error) {
// ...
}
}
```
Which is called [within the exposed `@angular/core` `detectChanges` function](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/view_ref.ts#L273-L275):
```typescript
// Angular 15 source code
// angular/packages/core/src/render3/view_ref.ts
detectChanges(): void {
detectChangesInternal(this._lView[TVIEW], this._lView, this.context as unknown as {});
}
```
## Calling Change Detection Manually {#manual-cd}
Let's use [Angular's `NgZone`'s `runOutsideOfAngular`](https://angular.io/api/core/NgZone#runOutsideAngular) to run some code outside of Angular's typical change detection:
```typescript
import { ApplicationRef, Component, NgZone } from '@angular/core';
@Component({
selector: 'my-app',
template: `
Hello {{name}}
`,
})
export class AppComponent {
constructor(private ngZone: NgZone) {}
name = '';
changeName() {
this.ngZone.runOutsideAngular(() => {
setTimeout(() => {
this.name = 'Angular';
});
});
}
}
```
> Don't worry if you're not already familiar with `NgZone`, we'll explain how it works fully in this article. 😄
Here, you'll notice that when you press the `