Discovering the User Timing API

Share this article

A couple of months ago, I gave a talk about some HTML5 APIs which partially focused on how to measure performance. One technique is via the High Resolution Time API, an API described in a previous article of mine. The API allows you to retrieve the current time in sub-millisecond resolution without being subject to system clock skew or adjustments, which in turn enables us to accurately measure the performance of our JavaScript code. While this API is useful, it leaves us with the pain of introducing a bunch of variables in our code. In addition, if you want to measure the performance of code that is split among several files or modules, you must introduce global variables. To solve these problems, we can employ the User Timing API.

What’s the User Timing API

The User Timing API is defined as an interface to help web developers measure the performance of their applications by giving them access to high precision timestamps. Stated in other words, this API allows you to accurately measure and report the performance of JavaScript code, which is perfect when you need to benchmark your code. As of this API is a W3C Recommendation. Therefore, the specifications are stable and you can expect few changes. This API deals with two main concepts: Mark and Measure. The first implements the PerformanceMark interface, while the second implements the PerformanceMeasure interface. Both these interfaces, extend the PerformanceEntry interface. A Mark represents an instant (timestamp), while a Measure represents the time elapsed between two Marks. Because both of them extend the PerformanceEntry interface, they own the same four, read-only properties:
  • name: A mnemonic name associated with the Mark or the Measure that is used to retrieve it.
  • entryType: Specifies the type of the object, that is if it’s a Mark or a Measure.
  • startTime: If the object is a Mark, this is a DOMHighResTimeStamp, a highly accurate timestamp retrieved using the performance.now() method of the High Resolution Time API. If the object is a Measure, it contains the DOMHighResTimeStamp of the start Mark of the Measure.
  • duration: If the object is a Mark, the value is always 0 (zero). If the object is a Measure, it contains the time elapsed between the two Marks.
This User Timing API exposes four methods that belong to the window.performance object. They are:
  • mark(name): Stores a DOMHighResTimeStamp with the associated name.
  • clearMarks([name]): Deletes one or all the stored Marks.
  • measure(name[, mark1[, mark2]]): Stores the time elapsed between two Marks with the provided name.
  • clearMeasures([name]): Deletes one or all of the stored Measures.
Please note that the names passed to the mark()
and the measure() functions are not unique IDs. You can use the same name as many times as you want. In this case, when you perform a name retrieval, an array sorted by the startTime property is returned. Before moving forward, let’s see a simple example that employs some of the methods described. Let’s say that we want to measure the execution time of a function and then delete the measurement without displaying the data. The code to perform this task is shown below:
performance.mark("startFoo");
// A time consuming function
foo();
performance.mark("endFoo");

performance.measure("durationFoo", "startFoo", "endFoo");

// Delete all Marks
performance.clearMarks();
// Delete the Measure "durationFoo"
performance.clearMeasure("durationFoo");
This snippet shows how we can call all of the previously introduced methods. However, storing timestamps and then deleting them without using the measurements is completely useless. To retrieve the data of the Marks and the Measures, we need to employ other two methods that belong to the Performance interface: getEntriesByType(type) and getEntriesByName(name). The former returns a list of entities of the type specified by the type parameter (ie. “mark” for Marks). The latter returns a list of the entities with the name specified by the name parameter. Both of them return the list sorted based on the startTime property.

Browser Support

Support for this API is decent both on desktop and mobile browsers. In addition, those that do support this API don’t employ a vendor prefix. The desktop and mobile browsers that implemented the User Timing API are Internet Explorer 10+, Chrome 25+, and Opera 15+. However, we can expect Firefox to support it very soon because of its current stage in the W3C recommendation process. “OK, but what if I want to use this API in browsers that don’t support it?” Glad you asked! Fortunately for us, there is a polyfill called usertiming.js that allows us to use the previously described methods. The bad news is that this polyfill only works in browsers that support the High Resolution Time API and its performance.now() method.

Demo

This section provides a simple demo that allows you to experiment with the concepts explained in this article. The demo defines a simple form with two input fields. Inside them, we have two numbers that we’ll use to simulate a time consuming function of a given duration. We also test for browser support and display an “API not supported” message if the user’s browser does not support the API. If the browser supports the User Timing API, we attach a listener to the click event of the button inside the form. Once clicked, we run the two simulated functions and store the timestamps. Then, we measure the elapsed time and display some of the stored information. A live demo of the code below is available here.
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="author" content="Aurelio De Rosa">
    <title>User Timing API Demo by Aurelio De Rosa</title>
    <style>
      body
      {
        max-width: 500px;
        margin: 2em auto;
        font-size: 20px;
      }

      h1
      {
        text-align: center;
      }

      .hidden
      {
        display: none;
      }

      .field-wrapper
      {
        margin-bottom: 1em;
      }

      .button-demo
      {
        padding: 0.5em;
        display: block;
        margin: 1em auto;
      }

      .author
      {
        display: block;
        margin-top: 1em;
      }
    </style>
  </head>
  <body>
    <h1>User Timing API</h1>
    <form>
      <div class="field-wrapper">
        <label for="count-ut-1">Test 1: Count until</label>
        <input type="number" id="count-ut-1" value="100000" />
      </div>

      <div class="field-wrapper">
        <label for="count-ut-2">Test 2: Count until</label>
        <input type="number" id="count-ut-2" value="1000000" />
      </div>

      <button type="button" id="button-play-ut" class="button-demo">Run demo</button>
    </form>
    <span id="ut-unsupported" class="hidden">API not supported</span>
    <div id="ut-results" class="hidden">
      <h2>Marks</h2>
      <div id="ut-marks"></div>
      <h2>Measures</h2>
      <div id="ut-measures"></div>
    </div>

    <small class="author">
      Demo created by <a href="http://www.audero.it">Aurelio De Rosa</a>
      (<a href="https://twitter.com/AurelioDeRosa">@AurelioDeRosa</a>)
    </small>

    <script>
      window.performance = window.performance || {};
      if (performance.mark === undefined) {
        document.getElementById('ut-unsupported').classList.remove('hidden');
        document.getElementById('button-play-ut').setAttribute('disabled', 'disabled');
      } else {
        document.getElementById('button-play-ut').addEventListener('click', function() {
          var i;
          var perfMarks;
          var perfMeasures;

          document.getElementById('ut-results').classList.remove('hidden');
          // A time consuming function
          performance.mark("startTime1");
          for(i = 0; i < parseInt(document.getElementById('count-ut-1').value); i++);
            performance.mark("endTime1")

            // Another time consuming function
            performance.mark("startTime2");
            for(i = 0; i < parseInt(document.getElementById('count-ut-2').value); i++);
              performance.mark("endTime2");
              performance.measure("durationTime1", "startTime1", "endTime1");
              performance.measure("durationTime2", "startTime2", "endTime2");
              performance.measure("durationTimeTotal", "startTime1", "endTime2");

              // Print marks
              perfMarks = performance.getEntriesByType("mark");
              document.getElementById('ut-marks').innerHTML = '';
              for (i = 0; i < perfMarks.length; i++) {
                document.getElementById('ut-marks').innerHTML +=
                  "Name: " + perfMarks[i].name + " - " +
                  "Start Time: " + perfMarks[i].startTime + "<br />";
              }

              // Print measures
              perfMeasures = performance.getEntriesByType("measure");
              document.getElementById('ut-measures').innerHTML = '';
              for (i = 0; i < perfMeasures.length; i++) {
                document.getElementById('ut-measures').innerHTML +=
                  "Name: " + perfMeasures[i].name + " - " +
                  "Duration: " + perfMeasures[i].duration + "<br />";
              }
              performance.clearMarks();
              performance.clearMeasures();
        });
      }
    </script>
  </body>
</html>

Conclusion

This article has explored the User Timing API and showed how it can help you in testing the performance of your JavaScript code. Performance is really important and we should fight for even the slightest improvement. This API doesn’t not introduce too many concepts, so it should not be hard for you to digest its properties and methods. In addition, its support among browsers is pretty good, so you can use it reliably right now. However, for those that don’t support the User Timing API (most notably Firefox), a polyfill is available.

Frequently Asked Questions (FAQs) about User Timing API

What is the User Timing API and why is it important?

The User Timing API is a powerful tool that allows developers to measure the performance of specific parts of their applications. It provides a more granular level of detail compared to other performance APIs, enabling developers to accurately measure and analyze the time taken by specific operations within their code. This is crucial in optimizing application performance and improving user experience, as it helps identify bottlenecks and areas that need improvement.

How does the User Timing API differ from other performance APIs?

Unlike other performance APIs, the User Timing API allows developers to create their own custom timestamps, known as “marks”, and measure the time between these marks, known as “measures”. This provides a more detailed and customizable way of tracking performance, allowing developers to focus on specific parts of their application.

How do I use the User Timing API in my application?

To use the User Timing API, you first need to create a “mark” at the point in your code where you want to start measuring. This is done using the performance.mark() method. You can then create a “measure” to calculate the time between two marks using the performance.measure() method. These measures can then be retrieved and analyzed using the performance.getEntriesByType("measure") method.

Can I use the User Timing API to measure the performance of asynchronous operations?

Yes, the User Timing API can be used to measure the performance of asynchronous operations. By placing a mark at the start and end of the asynchronous operation, you can create a measure that accurately reflects the time taken by the operation.

What are some common use cases for the User Timing API?

The User Timing API is commonly used to measure the performance of specific parts of an application, such as the time taken to load a page, render a component, or complete a database query. It can also be used to track the performance of asynchronous operations, such as AJAX requests or promises.

How can I analyze the data collected by the User Timing API?

The data collected by the User Timing API can be analyzed using the performance.getEntriesByType("measure") method, which returns an array of PerformanceEntry objects. Each object contains information about a measure, including its name, start time, and duration.

Can I use the User Timing API in all browsers?

The User Timing API is supported by most modern browsers, including Chrome, Firefox, Safari, and Edge. However, it may not be supported in older browsers or certain mobile browsers.

How can I use the User Timing API to improve my application’s performance?

By using the User Timing API to measure the performance of specific parts of your application, you can identify areas that are slow or inefficient and focus your optimization efforts on these areas. This can lead to significant improvements in overall application performance and user experience.

Can I use the User Timing API in conjunction with other performance APIs?

Yes, the User Timing API can be used in conjunction with other performance APIs, such as the Navigation Timing API and the Resource Timing API, to provide a comprehensive view of your application’s performance.

What are some limitations of the User Timing API?

While the User Timing API is a powerful tool for measuring performance, it does have some limitations. For example, it can only measure the time between marks within the same browsing context, and it does not provide any information about memory usage or CPU usage.

Aurelio De RosaAurelio De Rosa
View Author

I'm a (full-stack) web and app developer with more than 5 years' experience programming for the web using HTML, CSS, Sass, JavaScript, and PHP. I'm an expert of JavaScript and HTML5 APIs but my interests include web security, accessibility, performance, and SEO. I'm also a regular writer for several networks, speaker, and author of the books jQuery in Action, third edition and Instant jQuery Selectors.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week