Sunday, February 9, 2014

Use Zone to Trigger Angular Digest Loop for External Functions

To continue my series on the power of Zone, I examine yet another powerful and useful way you can use zones to improve your application. If this is your first time learning about Zone, read my introduction to Zone titled Taming Asynchronous Tasks in JavaScript with Zone.js. Anyone familiar with Angular apps has run into the concept of the $digest loop. This is essentially a pass to update data-binding. When the model is mutated, which can happen a number of ways, anything observing the model is notified. The watchers may also mutate the model further which results in a recursive call until either the changes settle or the maximum recursion is reached.

The problem with this approach is that Angular is only aware of changes that happen within the loop. Directives that ship with Angular are automatically called within the loop so their changes are propagated. It is not uncommon to introduce a third-party library that may mutate the model somehow. When this happens, Angular is not aware of the changes because it happens outside of the loop. When you are able to intercept the update yourself, you can make it inside of a call to $apply which will notify Angular of the change. However, you don’t always have the luxury of intercepting third-party modules.

To demonstrate how Zone can help, I created a simple (contrived) scenario. Assume you have a simple clock you wish to display:

<div ng-app="myApp" ng-controller="myController">
    {{timer.time | date:'HH:mm:ss'}}
</div>

The timer, however, comes from a third party control. It exposes a timer object and runs on a timer but you can only kick it off – you have no control of the actual code that makes the updates. Again, to keep it simple, assume this amazing control works like this (remember, you only have access to the externalTimeObj, and something else kicks it off).

var externalTimeObj = {
    time: new Date()
};
setInterval(function() {
    externalTimeObj.time = new Date();
}, 1000);

In Angular, you can capture the timer and place it on your scope:

var app = angular.module("myApp", []);
app.value("timerObj", externalTimeObj);
app.controller("myController", function($scope, timerObj) {
    $scope.timer = timerObj;
});

But the problem is you don’t see any updates (here’s the proof). Even though the timer object is updating, it happens outside of the digest loop so Angular isn’t aware. What’s worse is that the only way you can update your timer to call $apply is to get in and modify the source code which isn’t ever a great idea. If only there was a way to create an execution context that would automatically update the digest loop when done. Then you could call the initialization methods for the third-party timer control from within that execution context and capture the updates. Wait, there is! We have Zone.

First, create a zone that is dedicated to initiating the digest loop when it’s work is finished. OK, don’t, because I’ve already done it and it looks like this:

var digestCapture = null;
 
var digestZone = (function () {
    return {
        digest: function() { },
        onZoneEnter: function () {
            if (digestCapture) {
                zone.digest = digestCapture;
                zone.onZoneEnter = function() {};
            }
        },
        onZoneLeave: function () {
            zone.digest();
        }
    };
}());

The digestCapture variable is necessary to allow Angular to initialize the call into the digest loop. If we bootstrap Angular in this zone we’ll create too much overhead because Angular methods that already execute in the digest loop will trigger a redundant call when the task is complete. Notice how once the digest call is captured, the check for it is removed by replacing the onZoneEnter function with a no-op.

There is no need to change any of the Angular code (that’s why I love this approach – open to extensibility, closed to change). Simply add the code to capture the digest function:

app.run(function($rootScope){
    digestCapture = function() {
        $rootScope.$digest();
    };
});

There is also no need to change the third-party control. Instead, we just move our third-party control initialization into the zone (again, we’re using the interval here but this could be any call into the third-party API as once it happens in a zone it is captured for that zone).

zone.fork(digestZone).run(function() {
    setInterval(function() {
        externalTimeObj.time = new Date();
    }, 1000);
});

That’s it! Now we’ve got a way to initialize our third-party controls and ensure any time they return from an asynchronous task that we are able to notify Angular our model has mutated by running the digest loop. See the code running yourself.

Saturday, February 8, 2014

Instrumenting Angular with Zone

In my last post I described an open source tool from the Angular team called Zone that allows you to execute a JavaScript workflow within an execution context. I demonstrated how to instrument a sequence of asynchronous events using Zone. This is a short post to follow-up and illustrate how to do the same thing in Angular.

The first step, of course, is to Angular-ize the HTML. This is simple enough. Instead of manually binding a click function, I can use the ng-click directive, and instead of manually setting the data I can use data-binding.

<div>
    <button id="myBtn" ng-click="populate()">Populate</button>
    <span id="myData">{{data}}</span>
</div>


In order to put Angular “into the Zone” we need to capture the bootstrap process. By default Angular will run and find directives to bind to, but we want to defer this step so that it runs in the context of a zone. To keep the example simple I’m not using a controller and will just set everything up on the $rootScope.

var main = function() {
    zone.marker = "main";
    console.log('getting injector');
    angular.module('myApp', []).run(function($rootScope, $timeout){
        zone.marker = "module run";
        $rootScope.populate = function() {
            zone.marker = "click";
            $rootScope.data = "Initializing...";
            $timeout(function() {
                zone.marker = "timeout";
                $rootScope.data = "Done";
            }, 2000);
        };
    });
    angular.bootstrap(document, ['myApp']);
};


That’s really it. This should look very similar to the previous example. Note that besides the zone “markers” I set up to make the console output more readable, there is nothing that changes in the Angular code itself – everything is handled by virtue of it being run from within a zone. The zone is fired up the same way as the previous example:

zone.fork(profilingZone).run(function() {
    zone.reset();
    main();
});


When it’s run you can see we get a sequential profile of the asynchronous events … for example, notice the time elapsed between the last two is almost exactly 2 seconds or the interval that I set using Angular’s $timeout service.

Entered task
Exited task module run after 22.999999986495823
Total active time: 22.999999986495823
Total elapsed time: 22.999999986495823
Entered task
Exited task click after 3.9999999571591616
Total active time: 26.999999943654984
Total elapsed time: 4333.999999973457
Entered task
Exited task timeout after 0.9999999892897904
Total active time: 27.999999932944775
Total elapsed time: 6333.999999973457

The full fiddle for this example is here. I included the Zone source because I’m not aware of a CDN for it yet.

Thursday, February 6, 2014

Taming Asynchronous Tasks in JavaScript with Zone.js

I recently learned about a new project by the Angular team called Zone. This project is one of those rare gems that is only a few lines of code but is so groundbreaking it literally takes time to wrap your mind around it. The easiest way to get started with Zone is to watch the excellent talk by its creator, Brian Ford. He demos some pretty cool scenarios that are all in the project repository.

In a nutshell, Zone provides what you might consider a “thread execution” context for JavaScript. It basically takes the current “context” you are in and intercepts all asynchronous events so they “map” to the same context. This enables you to do some pretty interesting things such as view stack traces through the original wire-up (i.e. you no longer get “disconnected” at the point the event was raised) or add tooling. You can use Zone to change the behavior of events (such as implementing your own version of setTimeout) and to keep track of tasks.

Zone works by simply running something in the context of the Zone. You can have multiple Zones with their own context. To steal an example from the Zone repo, consider this:

zone.run(function () {
  zone.inTheZone = true;

  setTimeout(function () {
    console.log('in the zone: ' + !!zone.inTheZone);
  }, 0);
});

console.log('in the zone: ' + !!zone.inTheZone);

Notice that you can set properties and call an asynchronous function and still have access to the properties in the context of the Zone but once you fall out you are no longer in the zone.

This gives rise to some very intriguing possibilities. As with all frameworks, the only way I knew I could wrap my arms around Zone is by building my own example so I’ll walk you through that and you can understand one of the many “use cases” for Zone.

I started with the idea of a simple HTML fragment that simply contains a button and an area to hold some data:

<div>
    <button id="myBtn">Populate</button>
    <span id="myData">Nothing</span>
</div>

Then I went old school with some JavaScript functions. I attach an event handler to the button and when you click it, it updates the data and sets a timer. The timer fires after 2 seconds and updates the data again. It looks like this:

var main = function () {
    
var btn = document.getElementById("myBtn"
),
         data = document.getElementById(
"myData"
);
     btn.addEventListener(
"click", function
() {
         data.innerHTML =
"Initializing..."
;
         setTimeout(
function
() {
             data.innerHTML =
"Done.";
         }, 2000);
     }); };

At this stage you can call the main method and see the app work. Let’s assume you wanted to time how long it is taking to wire things up (maybe you just invented a cool new data-binding framework, for example). How do you keep track of asynchronous events and capture them in the correct order? No worries. Don’t bother with writing that library. Just pull in Zone.

One nice thing about Zone is that you can create a template to intercept actions on the zone. You can store specific variables or functions on the zone, but more importantly you can implement functions that are called when the zone is entered and exited. This happens any time a function is executed in the context of the zone, including asynchronous functions. Borrowing from a performance example in the repo, I created a template for a profiling zone that picks up the best counter available to the browser:

var time = 0,
    
// use the high-res timer if available     timer = performance ?
             performance.now.bind(performance) :
             Date.now.bind(Date);

Then I return a zone template. Notice I created my own property called “marker” that I’m using to tag where I’m at. In the code I showed earlier, I’ll tag the zone by inserting zone.marker = “main” and zone.marker = “click” and zone.marker = “timeout” at the beginning of each function. This will give me some useful information later.

Here is the Zone template that I return:

return {
     marker:
"?"
,
     onZoneEnter:
function
() {
        
this.originalStart = this
.originalStart || timer();
        
this
.start = timer();
         console.log(
"Entered task"
);
     },
     onZoneLeave:
function
() {
        
var diff = timer() - this
.start,
             totalDiff = timer() -
this
.originalStart;
         console.log(
"Exited task " + zone.marker + " after "
+ diff);
         time += diff;
         console.log(
"Total active time: "
+ time);
         console.log(
"Total elapsed time: "
+ totalDiff);
     },
     reset:
function () {
         time = 0;
     } };

Now all I have to do is wrap things up in Zone. Because the context handles everything within in, there are no modifications to the main method. Zone will handle taking all of the events and marshaling them into my context for me. The only thing I need to do other than pulling in the Zone code itself is to call main like this:

zone.fork(profilingZone).run(function () {
     zone.reset();
     main(); });

This forks a copy based on my template and runs the main function in the context. Now I just run the example, wait a few seconds, and click the button. Here’s what shows up in my console. Note I did not modify my main method except to set the markers. All of the instrumentation was added automatically by the zone. Note total elapsed time is large because I literally ran this and waited as I authored the article before I clicked the button. That’s OK because the zone was able to keep track!  The difference between the last two total elapsed times is about 2 seconds or the interval I set on the timeout.

Entered task
Exited task main after 2.0000000076834112
Total active time: 2.0000000076834112
Total elapsed time: 2.0000000076834112
Entered task
Exited task click after 2.0000000076834112
Total active time: 4.0000000153668225
Total elapsed time: 830135.0000000093
Entered task
Exited task timeout after 0
Total active time: 4.0000000153668225
Total elapsed time: 832138.0000000063

How powerful is that? One common complaint about Angular is the digest loop that is used to scan models and listeners and look for changes in the model. It is required in order to respond to external changes and a special method must be called (apply) when you are mutating the model outside of this loop. Sometimes the loop must fire multiple times in order to be certain side effects are picked up (i.e. a change here causes a change there which means another place must be updated). Zone will enable the Angular team to fire the entire cycle inside a zone context and the team believes that will eliminate the need for digest or apply entirely!

To see this for yourself, check out this fiddle. I don’t know of a CDN for Zone yet so I just included the entire source in the fiddle.

Sunday, February 2, 2014

Video: Angular and the .NET World

While I was at the Angular ng-conf I had the opportunity to sit down with .NET luminaries Ward Bell, John Papa, and Dan Wahlin to discuss why Angular is so important to .NET developers with Ian Smith. We cover lots of ground, including real world benefits we've received by integrating Angular into projects and how we see solid development patterns we used in Silverlight and other XAML technologies carry forward to the web through Angular. I consider this discussion a must see for anyone working in enterprise or line of business web development who is looking to modernize their current application by taking advantage of new HTML5 technologies.

Interesting in learning more? Here are some Angular resources and my own Mastering Angular course.

Jeremy Likness