Introduction to the Resource Timing API

SitePoint has published some good articles on performance recently. In particular, Craig Buckler wrote an article entitled Average Page Weights Increase by 32% in 2013 along with another one discussing some easy wins to reduce the weight of a website.

But improving the speed of our websites and apps isn’t just a matter of minifying JavaScript files and employing an image compressor. In many cases we have to deal with external resources (libraries from a CDN, videos from YouTube, images from Flickr, and so on) or internal resources that need to be accurately measured to establish what’s slowing down the loading of our pages.

I’ve previously covered some APIs that can help in testing the performance of a website. For example, the High Resolution Time API and the User Timing API. In this article, I’ll introduce you to another API in this category: The Resource Timing API.

What’s the Resource Timing API?

The Resource Timing API allows you to collect complete timing information related to resources in a document. It enables you to measure user latency, which is crucial to benchmark web pages. This API is a W3C Candidate Recommendation so we can be sure that significant features are mostly locked, but there could be minor changes to the spec in the future.

Using the Resource Timing API allows us to retrieve and analyze a detailed profile of all the network timing data for every resource on the page. We’ll cover what these data are in the next section. For the moment, it’s important that you understand how this API can help us in tracking the performance of the resources of our page, and determine how many and what resources we have to optimize.

Now that we know what this API is, let’s delve into its features.

Methods, Properties, and Events

The Resource Timing API is exposed through the performance property of the window object. We can retrieve the information collected for each resource using the getEntriesByType() method. If this sounds familiar to you, this is because it’s the same method used by the User Timing API. The difference is that to retrieve information about these resources we have to pass the string resource to getEntriesByType().

The getEntriesByType() method returns a list of PerformanceResourceTiming objects, which extend the PerformanceEntry interface. Because of this, each of these objects expose the following inherited properties:

  • name – The resolved URL of the requested resource
  • entryType – The value is always equal to resource
  • startTime – The time immediately before the user agent starts to queue the resource for fetching
  • duration – The difference between responseEnd and startTime

In addition to these properties, the Resource Timing API exposes another property called initiatorType. If the request has been initialized by an element on the page, its value is equal to the name of the tag (e.g. img for <img>, script for <script>, and so on). Other possible values are css (for CSS resources downloaded using the url() syntax — e.g. background: url(...)) and xmlhttprequest (for XMLHttpRequest objects).

Each PerformanceResourceTiming object provides the following specific and read-only attributes:

  • initiatorType
  • redirectStart
  • redirectEnd
  • fetchStart
  • domainLookupStart
  • domainLookupEnd
  • connectStart
  • connectEnd
  • secureConnectionStart
  • requestStart
  • responseStart
  • responseEnd

The following image offers a graphical representation of these attributes. Those that are underlined may not be available when fetching resources from different origins:

Illustrating the timing attributes defined by the PerformanceResourceTiming interface

Resources fetched from a third-party must provide an additional HTTP header (Timing-Allow-Origin: *) to allow the site to gather detailed network timing data. If the header is absent, the only available data is the total duration of the request. This may seem like an important limit to the use of this API. However, as Ilya Grigorik wrote in his post Measuring network performance with Resource Timing API, several websites like Google, Facebook, and Disqus, have implemented the header to offer this information.

As we’ve seen, the Resource Timing API provides a lot of attributes that we can read to understand where the time is spent for each resource. However, it also provides two methods: clearResourceTimings() and setResourceTimingBufferSize(). The former clears the buffer used to store the current list of PerformanceResourceTiming resources.

The latter sets the maximum number of objects stored in the buffer. It accepts an integer to specify the limit. If the method isn’t called explicitly, the specification states that the user agent should store at least 150 PerformanceResourceTiming resources. When the limit of the resources to store is reached, the API fires an event called onresourcetimingbufferfull.

Now that we’ve learned the attributes, methods, and events exposed, let’s see how many browsers support this API.

Browser Compatibility

Browser support for this API is decent on desktop as it has been implemented in Chrome 25+, Internet Explorer 10+, and Opera 15+. On mobile, the situation is very similar with the addition of the latest versions of the Android browser.

Testing if a browser supports this API is a bit tricky because we have to test for several conditions. First we have to test for the presence of the performance property of the window object. Then, we have to test for the presence of the getEntriesByType() method and that it’s able to collect resources. This last condition can be verified by checking that the call to getEntriesByType('resource') returns an array.

Turning this description into code results in the following snippet:

if ( !('performance' in window) ||
    !('getEntriesByType' in window.performance) ||
    !(window.performance.getEntriesByType('resource') instanceof Array)
  ) {
  // API not supported
} else {
   // API supported. Hurray!
}

Building a Demo

In this section we’ll build a simple demo that allows us to see this API in action and the information it provides. The demo loads two resources: an image from SitePoint.com included via an <img> tag, and the jQuery library from the Google CDN via a <script> tag.

While both are external resources, the latter allows us to gather timing information thanks to the Timing-Allow-Origin: * header. This means that even if it’s an external resource, we’ll obtain all the information exposed by the API.

With the markup in place, the first thing we have to do is to test if the browser supports the Resource Timing API. If the API isn’t supported, we display the message “API not supported”. In case the browser implements the API, we attach a listener to the load event of the window object. In this way we’re sure to execute any action after all the resources are loaded. Inside the handler, we create a list on-the-fly to show the measures obtained through the API.

Our demo page will use the following HTML:

  <span id="rt-unsupported" class="hidden">API not supported</span>

  <div>
     <h2>Test 1 - jQuery</h2>

     <ul id="script-list">
     </ul>
  </div>
  <div>
     <h2>Test 2 - Image</h2>

     <img src="http://www.sitepoint.com/wp-content/themes/sitepoint/assets/svg/sitepoint.svg" />
     <ul id="img-list">
     </ul>
  </div>

  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

And here’s the JavaScript that will display some data that we can examine, which will show us what the API can do:

  if ( !('performance' in window) ||
       !('getEntriesByType' in window.performance) ||
       !(window.performance.getEntriesByType('resource') instanceof Array)
     ) {
     document.getElementById('rt-unsupported').classList.remove('hidden');
  } else {
     window.addEventListener('load', function() {
        var resources = window.performance.getEntriesByType('resource');
        for(var obj in resources) {
           var list = '';
           for(var properties in resources[obj]) {
              list += '<li>' + properties + ': <span class="value">' + resources[obj][properties] + '</span></li>';
           }
           document.getElementById(resources[obj].initiatorType + '-list').innerHTML = list;
        }
     });
  }

You can view the code live here.

Conclusion

As we’ve seen, using this API shouldn’t be too difficult for you to employ it in a future project. Unfortunately, the support among browsers is not ideal, but the fact that three of the major browsers (Chrome, Opera, and Internet Explorer) supports it is still good news.

There are no more excuses for failing to improve the performance of your website, and this new API will make it that much easier.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • joolythomas

    I just got paid $7500 working off my computer this month. And if you think that’s cool, my divorced friend has twin toddlers and made over $8k her first month. It feels so good making so much money when other people have to work for so much less.

    Here ­­­­­­­­­is ­­­­­­­­­I ­­­­­started>>>>>>>>>➜➜➜➜➜➜➜

    ➜➜➜➜ W­W­W­.­N­E­T­P­A­Y­1­0­.ℭ­ℴ­m

    —————————————————–

    GO TO THE SITE –>>>CLICK NEXT TAB FOR MORE INFO AND HELP

  • Prashant Palikhe

    Great explanation. One thing though, the results are in milliseconds. For the resources that are already in the browser cache, how come it shows that there is absolutely no latency involved. zero. Caches are maintained in HDD/SDD I assume and would therefore require a system call to the OS to fetch them. I would expect some latency there too. Right?

    • LouisLazaris

      I honestly can’t say I know the answer to this question, but it is a good question.

      My guess is that this API is only dealing with latency directly involved with the API itself and how the browser integrates it, rather than going past that into OS capabilities. So yeah, I’m sure there is some latency on the OS side, but that’s probably not something that can be accessed via this API. After all, wouldn’t that be a security vulnerability if this API could access your hard drive’s latency info? But again, that’s just my currently ignorant answer; I really don’t know without doing the research.

    • Aurelio De Rosa

      Hello Prashant Palikhe.

      Even if LouisLazaris says he’s “ignorant” about this matter, he’s experience gave him the power to reply in the right way. This API is meant to measure the latency of HTTP(s) request, not of the Operating System.

      This could also be inferred by the note of Ilya in his post:

      > Note: due to long cache lifetime of some of the assets that are now Resource Timing enabled, some users may not be able to get immediate access to timing data as they may be using a cached copy. This will resolve itself as more users update their resources.

      Hope this answer your question.

  • http://www.emarketeur.fr Gregory R

    Good to know. Honestly never heard of this API before.

    Could that be a path to explore in the neverending quest for PhoneGap performance issues ?

    • Aurelio De Rosa

      Hello Gregory.

      I think the use of this API is more application-specific than framework-specific. Even if two apps are using PhoneGap, the first could be very fast and the second very slow because it all depends on the resources they are using.

  • LouisLazaris

    This looks cool, nice job…. But it would be nice if the data was already expanded. Having to hover over those little areas is kind of annoying… Is it too much to display everything at once? Hovering isn’t really ideal in such a situation, as it’s confusing.

    You should definitely improve the look of the data and maybe allow the bits of data to appear on click, rather than hover, or just show it all at once.