Using Performance Timeline

The Performance Timeline standard defines extensions to the Performance interface to support client-side latency measurements within applications. The standard also includes interfaces that allow an application to be notified when specific performance events occur. Together, these interfaces can be used to help identify an application's performance bottlenecks.

Performance extensions

Performance Timeline extends the Performance object with three methods that provide different mechanisms to get a set of performance records (metrics), depending on the specified filter criteria. The following example show the usage of these methods getEntries(), getEntriesByName() and getEntriesByType().

function log(s) {
  const o = document.getElementsByTagName("output")[0];
  o.innerHTML += `${s} <br>`;
}

function doWork(n) {
  for (let i = 0; i < n; i++) {
     const m = Math.random(); // This is an example of work taking some time
  }
}

function printPerfEntry(pe) {
  log(`name: ${pe.name}`);
  log(`entryType: ${pe.entryType}`);
  log(`startTime: ${pe.startTime}`);
  log(`duration: ${pe.duration}`);
}

function printPerformanceEntries() {
  if (performance.mark === undefined) {
    console.error("The property performance.mark is not supported.");
    return;
  }

  // Create some performance entries via the mark() and measure() methods
  performance.mark("Begin");
  doWork(50000);
  performance.mark("End");
  doWork(50000);
  performance.measure("Measure1", "Begin", "End");

  // Use getEntries() to iterate all entries
  performance.getEntries()
    .forEach((entry, i) => {
      log(`All Entry[${i}]`);
      printPerfEntry(entry);
    });

  // Use getEntries(name, entryType) to get specific entries
  performance.getEntries({ name: "Measure1", entryType: "measure" })
    .forEach((entry, i) => {
      log(`Begin and Measure [${i}]`);
      printPerfEntry(entry);
    });

  // Use getEntriesByType() to get all "mark" entries
  performance.getEntriesByType("mark")
    .forEach((entry, i) => {
      log(`Mark only [${i}]`);
      printPerfEntry(entry);
    });

  // Use getEntriesByName() to get all "mark" entries named "Begin"
  performance.getEntriesByName("Begin", "mark")
    .forEach((entry, i) => {
      log(`Begin and Mark [${i}]`);
      printPerfEntry(entry);
    });
}

PerformanceEntry interface

The PerformanceEntry interface encapsulates a single performance entry i.e. a single performance metric. This interface has four properties and a JSON serializer (toJSON(). The following example shows the use of these properties.

function printPerformanceEntry(ev) {
  const properties = ["name", "entryType", "startTime", "duration"];

  // Create a few performance entries
  performance.mark("Start");
  doWork(50000);
  performance.mark("Stop");
  performance.measure("measure-1");

  performance.getEntries()
    .forEach((perfEntry, i) => {
      log(`PerfEntry[${i}]`);
      properties.forEach((prop) => {
        // Check each property in window.performance
        const supported = prop in perfEntry;
        log(`${prop} = ${supported ? perfEntry[prop] : "Not supported"}`);
      });
    });
}

This interface also includes a toJSON() method that returns the serialization of the PerformanceEntry object. The following examples show the use of this method.

function perfEntryToJSON() {
  // Create a few performance entries
  performance.mark("mark-1");
  performance.mark("mark-2");
  performance.measure("meas-1", "mark-1", "mark-2");

  const peList = performance.getEntries();
  const pe = peList[0];

  if (pe.toJSON === undefined) {
    log ("PerformanceEntry.toJSON() is NOT supported");
    return;
  }

  // Print the PerformanceEntry object
  const json = pe.toJSON();
  const s = JSON.stringify(json);
  log(`PerformanceEntry.toJSON = ${s}`);
}

Performance Observers

The performance observer interfaces allow an application to register an observer for specific performance event types, and when one of those event types is recorded, the application is notified of the event via the observer's callback function that was specified at the time, the observer was created. When the observer (callback) is invoked the callback's parameters include a performance observer entry list that only contains observed performance entries. That is, the list only contains entries for the event types that were specified when the observer's observe() method was invoked.

The following example shows how to register two observers: the first one registers for several event types and the second observer only registers for one event type.

function PerformanceObservers() {
  // Create observer for all performance event types
  const observeAll = new PerformanceObserver((list, obs) => {
    // Print all entries
    list.getEntries().forEach((entry) => {
      printPerfEntry(entry);
    });

    // Print entries named "Begin" with type "mark"
    list.getEntriesByName("Begin", "mark").forEach((entry) => {
      printPerfEntry(entry);
    });

    // Print entries with type "mark"
    list.getEntriesByType("mark").forEach((entry) => {
      printPerfEntry(entry);
    });
  });

  // Subscribe to all performance event types
  observeAll.observe({
    entryTypes: ['frame', 'mark', 'measure', 'navigation', 'resource', 'server'],
  });

  // Create observer for just the "mark" event type
  const observeMark = new PerformanceObserver((list, obs) => {
    // Should only have 'mark' entries
    list.getEntries().forEach((entry) =>  {
      printPerfEntry(entry);
    });
  });

  // Subscribe to only the 'mark' event
  observeMark.observe({ entryTypes: ['mark'] });
}

function printPerfEntry(pe) {
  log(`name: ${pe.name}`);
  log(`entryType: ${pe.entryType}`);
  log(`startTime: ${pe.startTime}`);
  log(`duration: ${pe.duration}`);
}

The performance observer entry list interface has the same three getEntries*() methods as the Performance interface and these methods are used to retrieve observed performance entries within the observer callback. These methods have been used in the above stated example.

See also