What about NgZone, and ngrx? NgZone issues are not ease to catch. The only sign of failure is “from time to time UI is not updated at this place”. Ngrx since version 9 comes with a really nice feature to hunt these bugs down.

NgZone

Okay, so what is NgZone? You get known about it probably when you got your first ‘change-detection’ problem. Angular knows when you changed the properties set, and it’s able to react on it. For example by updating the rendered DOM. How does it work? I can explain it using a pattern that’s commonly in use in game dev or multi-threaded programming. Let say that you have a main thread called UI thread. Its responsibility is to render something on screen. Now you would like to trigger some expensive calculation from UI thread, and after all show the result. Let’s start from this basic code example:

function uiThread() {
    render(); // 1.
    requestAnimationFrame(uiThread);
}
1. the render method is responsible for UI logic. 
   We can assume that many components are involved here.

Now you would like to execute asynchronous action, and the action needs to be executed after render method. In a browser there is no way to do multi-threading as you can do it in Java, or C. Kind of “execute in another thread” mechanism can be done via setTimeout method. So let’s imagine that one of the classes at some point of execution chain needs to execute this:

setTimeout(() => {
    // some expensive computation 
    delegate.result =  computationResult;
}, 1000);

It’s triggered somewhere from a render(); method, and for some reason the result cannot be available in a middle of render() execution, or before it. The simplest solution is to use this pattern: (it’s simplified implementation only for article purpose)

const postTasks = []; // 1.

function uiThread() {
    render(); // 2.1
    while (postTasks.length) { // 4. 
        postTasks.pop()();  // 3. 
    }
    requestAnimationFrame(uiThread);
}

// 2.2 ...
setTimeout(() => {
    // some expensive computation 
    postActions.push(function() { // simple function, object instance, what-ever. Depends on case
          delegate.result =  computationResult;
    });
}, 1000);
1. array to store postponed actions
2.1 render&calculate UI to screen, execute our async action somewhere inside
2.2 example postponed execution, somewhere within `render()` implementation 
3. execute all 'tasks' from queue
4. clear actions queue, each 'tick' should come with clear state

Now you can be 100% sure that your action is executed within loop + after render(); method execution.

So what’s the NgZone?

Now imagine all of those angular components, and how they reacts dynamically to each other. This is not a magic, it’s just a change detection mechanism. By default, Angular is going through components tree, each time change detection is fired (Default change detection strategy) and it’s comparing&updating components state. The change detection is triggered for every action executed within angular zone - NgZone. The whole story :)

Angular has a few types of zones, like root zone, or forked zones. In general, it’s a kind of implementation of the above pattern. It’s even using requestAnimationFrame. This is why I’d like to show it to you first.

Useful NgZone methods

ngZone.runInZone - the classic one, you can use it to run something “in a zone”.
ngZone.runOutsideZone - run something without a zone (change detection triggering). Just don’t forget about it -> from time to time you can optimize stuff using it.

The classic example of “out of NgZone action” is any asynchronous action like addEventListener. Whenever you listen for not-angular async results, you need to take care of Zone manually.

NgRx + NgZone

It could be a bit surprise, but you cannot be 100% sure that your NgRx actions are executed within the zone. The unexpected “out of zone” executions can brings really catchy bugs into your application. But! there is a way to catch those bugs quickly:

@NgModule({
  imports: [
    StoreModule.forRoot(REDUCERS {
      runtimeChecks: {
        strictActionWithinNgZone: true
      }
    })
  ]
})
export class FooModule {}

You will get an error in case of “out-of-zone” action:

Action '[User UI] Logout' running outside NgZone.