Google Analytics Asynchronous Tracking: How it works – Peter Coles

Google Analytics Asynchronous Tracking: How it works 

The Google Analytics asynchronous tracking code came out of beta two weeks ago, and given my prior post on how scripts block page loads , I figured I’d briefly point out some interesting aspects of how the new tracking code works. The two things that I find interesting are (1) the asynchronous script loading and (2) the new syntax related to queuing events.

Which browsers support the async script attribute?

Here is the code that Google asks you to include, notice the “ga.async = true;” part:

var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-X']);
(function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript';
    ga.async = true;
    ga.src = ('https:'   == document.location.protocol ? 'https://ssl'   : 'http://www') + '';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);

It appears that setting the “async” attribute to true makes the magic happen, but if you look into which browsers currently support the async attribute , you’ll discover that it’s only Firefox 3.6+. So what gives?

It turns out that if you insert a script—that you created using document.createElement—dynamically into a page, then it will be asynchronous in most browsers. I ran some tests when this code was first released, and my results are included below.

Note: this table describes for each browser whether the the domContentLoaded event waits for an “async script” to finish before triggering, or if it goes ahead and triggers while the script is still loading. It does the same for window.load and also shows if a script that appears lower on the page will wait for an “async script” to load before executing or if it just goes ahead.

domContentLoaded event (jQuery) window.load event Execution of lower script on page
Safari 4.0.4 goes waits goes
FF 3.5.7 waits waits waits
FF 3.6 goes waits goes
Opera 10.10 waits waits waits
Chrome goes waits goes
IE6 goes goes goes
IE7 goes goes goes
IE8 goes goes goes
FF2 goes waits waits
FF3.6 goes waits goes
Chrome goes waits goes

As you can see, the most important actions—the domContentLoaded event firing and a lower script executing—can both happen before the “async script” finishes loading for all of these browsers except for Opera and Firefox less than 3.6. It’s mostly asynchronous… mostly.

The asynchronous syntax: _gaq.push ()

There’s a new syntax for tracking events, and also for setting the initial variables (like your account id) which looks like this:

var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-X']);

<a href="#" onClick="_gaq.push(['_trackEvent', 'Videos', 'Play', 'How is babby formed?']);">Play</a>

The magic happens when the script actually loads and then replaces the _gaq array with a new object that immediately executes all previously queued events and has a “push” method that will also immediately execute any event whenever it is called. The code for the new _gaq object looks something like this (a little simplified):

var GoogleAnalyticsQueue = function () {

    this.push = function () {
        for (var i = 0; i < arguments.length; i++) try {
            if (typeof arguments[i] === "function") arguments[i]();
            else {
                // get tracker function from arguments[i][0]
                // get tracker function arguments from arguments[i].slice(1)
                // call it!  trackers[arguments[i][0]].apply(trackers, arguments[i].slice(1));
        } catch (e) {}

    // more code here…

// get the existing _gaq array
var _old_gaq = window._gaq;

// create a new _gaq object
window._gaq = new GoogleAnalyticsQueue();

// execute all of the queued up events - apply() turns the array entries into individual arguments
window._gaq.push.apply(window._gaq, _old_gaq);

It’s a pretty cool idea that should make Computer Science professors who love interfaces really happy. If you want to read some more about the asynchronous tracking code, Mathias Bynens wrote a great post describing how to optimize the asynchronous Google Analytics snippet —it has some real passion for minimizing bits.

Extra: the “apply” method 

If you’re not familiar with the “apply” method, it can be called on any function and takes two arguments: (1) an object that should be treated as the this object inside the function and (2) an array that will be the arguments of the function. It’s being used in the above examples to convert arrays into the arguments of a function (almost like unpacking in Python, but not as elegant).

Also, if you want to use the “apply” method purely to set the this object inside a function, you should consider using “call” instead, which is slightly faster, but requires you to pass the arguments in directly (not in an array).

my_func.apply(this, ['foo', 'bar']);, 'foo', 'bar');