X
    Categories Performance Monitoring

Bedtime Stories with TBone.js

AppNeta no longer blogs on DevOps topics like this one.

Feel free to enjoy it, and check out what we can do for monitoring end user experience of the apps you use to drive your business at www.appneta.com.

TL;DR: Meet TBone, a reactive / data-flow-oriented programming library with a live templating system built on top of it, built by the team at AppNeta.  One part of TBone’s magic is that it searches the graph of model & view dependencies to prevent firing ajax requests when that data is not currently needed on the page.

Ever had the joy of inheriting an *awesome* web app cobbled together from a cornucopia of $.ajax calls, global event handlers, and a generous selection of various JQuery plugins, each one successively modifying and tweaking this and that throughout the page? Ever tried to extend or modify such an app? That’s what I did when I joined AppNeta last summer, wrangling order from DOM chaos.

But did I lay down and let the DOM manipulation party run me over? No. I built Yet Another Live Templating Library.

Meet TBone.

TBone is a reactive / data-flow-oriented programming library, with a live templating system build atop it. The core lets you do stuff like this:


T(function () {
    $('body').text(T('name.full'));
});
T('name.full', function () {
    return T('name.first') + ' ' + T('name.last');
});
T('name.first', 'Sally');
setTimeout(function () {
    T('name.last', 'Smith');
}, 1000);

Go ahead, try it out: http://tbonejs.org/#Full_Name

In this example, we’ve set up an auto-executing “T-function” at the top which will update the body element’s text to T(‘name.full’) whenever T(‘name.full’) changes. Next, we set up a “bound property” `name.full` to always be set to the concatenation of the first and last names. Whenever name.first or name.last change, this function will be re-run, and name.full will be set to the return value.

Next, we set name.first immediately. This triggers an update of name.full to “Sally undefined”, which in turn triggers the first T-function to fire, updating the body’s text as well. After another second, we update name.last. The same chain of events fires again, updating name.full and then the body’s text.

So, the first cool thing I like about TBone is that you don’t ever have to bind change events explicitly. You’re responsible for writing idempotent functions, and TBone is responsible for calling those functions as often as necessary.

The second cool thing, though, is that we can draw a graph of the data dependencies in this application.

What we’ve got here is a graph showing that our “view”, which I’ve labeled `body`, depends on `name.full`, and that `name.full` depends on `name.first` and `name.last`. Nifty.

If you build your whole application in this fashion, then you can graph the dependencies through your entire UI. Here’s a portion of the graph for a simple page in our app:

Here’s where we can start having some fun. The app I was working on made an average of ~12 ajax calls on any particular page/tab, each one loading data for a table or chart showing performance metrics for our clients’ web applications (server-side tracing). As you navigate around the app, we might swap out a few tables for another, or maybe change just the main visualization. Those 12 or so ajax calls are a select assortment of maybe 30 total endpoints that we call in some mix on every page throughout the app.

When the user adds a filter, we need to update all the charts and tables that are currently visible. We can do that by re-executing the $.ajax calls powering each of them. How do we know *which* ajax calls to re-execute? The solution in place was that there were four global events that could be fired whenever the user navigated around or added a filter. Each component on the page was responsible for listening to the appropriate events and updating itself. This worked, but there had to be a better way.

Enter The Sleeping XHR.

What if we used the dependency graph to determine whether a particular model’s data was needed by a visible view on the page?

Here, we have a list of six models. Each is an “ajax model,” backed by data from an ajax call. We’re only displaying data from three of them on the page, though, and so we only make the ajax calls for those three.

http://tbonejs.org/example/#The_Sleeping_XHR

We can draw the graph of dependencies for this – there are six ajax models, ajaxModel.0 through ajaxModel.5, and one view. The view depends only on ajaxModels 1, 2, and 3.

When each ajax model is about to send its ajax request, it does a depth-first-search of the dependency tree looking for an active view that depends on it.

If it doesn’t find any, as is the case for 0, 4, and 5, then that model goes to sleep without making the ajax request. And voila, we have a “smart” ajax model that will only fetch data when we need it, using automatically-maintained event bindings to figure it out.

Want to try to out? Check out the examples at tbonejs.org, download the source, and go to!

Dan Tillberg: Dan Tillberg is a web developer and author of TBone.js. He has a background in Physics and a penchant for data visualization and statistical analysis. Away from the office, Dan is passionate about user interfaces, automation, and using those skills together to cheat in RPGs. Find him on Twitter, Github, and tillberg.us.