Solving an Incident of Poor Performance in Angular 2 by Understanding Zone.js and How Async Calls Work in Angular 2
Sorry for the long title, but I just wanted to make it really clear what this post was going to be about. ;) In this post I'll basically show you how my straight-from-the-docs implementation for reading from the Firebase real-time database was resulting in a terribly slow-feeling user experience and how I was easily able to "make it work" once I had an understanding of how Angular 2, Zone.js, and Firebase (and asynchronous calls in general) are all interrelated in updating the DOM with the data in your typescript code.
A Little Setup
Our story begins with an Angular 2 application, of course! It will have an index.html, an NgModule, and uses @angular/router (although none of that it particularly important to the story). But just to give you some context, let's suppose we are on a page ( or route in Angular lingo), and the TypeScript here defines a component named CypherRoomComponent for that route:
The Bad Firebase Implementation
So in the above snippet we see methods like this.watchForChatUpdate(); which is an asynchronous call (meaning that the compiler fires it but cannot run the next line of code immediately. So, you create a callback that is triggered some time in the future. With Firebase in Angular 2 it might look like this:
The problem I was having with this implementation is that I wasn't seeing things fast enough. I vaguely remember one of Brad Green's inspiring Angular 2 conference talks, and I knew that if it wasn't smash-your-hand-into-your-palm fast then it must be me doing something wrong. It wasn't just sort of slow, though. I would have console.log statements in the callbacks so I could see that the response was coming back pretty much immediately (go Firebase), but it was taking 8-12 seconds for me to see an update on the screen! I even tried adding "| async" in the templates, but that just gave me new error messages. Yikes!
An Aside to Say that Gitter Is Awesome
Gitter (https://gitter.im) is an awesome service from Github where they basically create a chatroom for each Github project and organization. Sometimes you might be too embarrassed to post your question on stack overflow, on a forum, or to open an issue on an actual Github project. Plus, in a Gitter chatroom the people are there in real time (well, some are at least) so you can can help faster. Gitter has a nice interface and often the hardcore, very knowledgable coders in a particular area will hang out in that respective gitter chat room (which is good because then there's a better chance that someone will actually help you solve your issue).
The Performance Solution: Zone.js to the Rescue!
Basically, Zone.js is what Angular 2 uses for change detection under the hood, and Zone.js doesn't know about Firebase so when your async Firebase calls come back Zone.js isn't aware of them and isn't repainting the DOM (hence why I can see the logs immediately but the rendering takes so long). So, the gist of it is that you throw in another "this.zone.run (() =>" to let Zone.js know about your callbacks and you'll be good to go! Here's an example of how we can modify the previous snippet to let Zone.js know to repaint the DOM:
In order to use this.zone in your components don't forget that you need to inject an instance of NgZone in the constructor like this:
and import the NgZone class like this:
For more info on Zone.js definitely check out the Zone class in the Angular docs and the detailed Zone.js README on github with links to more great reads and even videos. :) Enjoy!
The posts on this site are written and maintained by Jim Lynch. About Jim...