Nutzertiming-API

Informationen zur Webanwendung

Alex Danilo

Hochleistungsfähige Webanwendungen sind entscheidend für eine gute Nutzererfahrung. Webanwendungen werden immer komplexer. Um eine überzeugende Nutzererfahrung zu schaffen, ist es daher wichtig, die Leistungsauswirkungen zu verstehen. In den letzten Jahren sind im Browser eine Reihe verschiedener APIs zur Analyse der Netzwerkleistung, Ladezeiten usw. erschienen. Diese liefern jedoch nicht unbedingt detaillierte Details mit ausreichender Flexibilität, um herauszufinden, was Ihre Anwendung verlangsamt. Die User Timing API bietet einen Mechanismus, mit dem Sie Ihre Webanwendung instrumentieren können, um zu ermitteln, wo Ihre Anwendung Zeit aufwendet. In diesem Artikel stellen wir die API vor und zeigen Beispiele für ihre Verwendung.

Was nicht gemessen werden kann, kann auch nicht optimiert werden

Der erste Schritt zum Beschleunigen einer langsamen Webanwendung besteht darin, herauszufinden, wo die Zeit verbraucht wird. Wenn Sie die Zeitauswirkung von Bereichen des JavaScript-Codes messen, können Sie Hotspots identifizieren. Dies ist der erste Schritt, um die Leistung zu verbessern. Mit der User Timing API können Sie API-Aufrufe an verschiedenen Stellen in Ihrem JavaScript-Code einfügen und dann detaillierte Zeitdaten extrahieren, die Sie zur Optimierung verwenden können.

Zeit bis zur Lösung und now()

Ein wesentlicher Bestandteil der genauen Zeitmessung ist die Präzision. Früher haben wir die Zeitmessung auf Millisekunden gestützt, was in Ordnung ist. Wenn Sie jedoch eine ruckelfreie Website mit 60 fps erstellen möchten, muss jeder Frame in 16 ms gerendert werden. Wenn Sie also nur eine Genauigkeit im Millisekundenbereich haben, fehlt es an der Genauigkeit, die für eine gute Analyse erforderlich ist. Geben Sie High Resolution Time ein, einen neuen Zeittyp, der in modernen Browsern integriert ist. High Resolution Time bietet uns Gleitkommazeitstempel mit einer Genauigkeit von Mikrosekunden – tausendmal besser als zuvor.

Wenn Sie die aktuelle Uhrzeit in Ihrer Webanwendung abrufen möchten, rufen Sie die Methode now() auf, die eine Erweiterung der Leistungsoberfläche darstellt. Im folgenden Code wird gezeigt, wie das geht:

var myTime = window.performance.now();

Die Benutzeroberfläche PerformanceTiming enthält eine Reihe verschiedener Zeitangaben, die sich auf das Laden Ihrer Webanwendung beziehen. Die Methode now() gibt die Zeit seit dem Zeitpunkt navigationStart in PerformanceTiming zurück.

Der Typ „DOMHighResTimeStamp“

Wenn Sie in der Vergangenheit Webanwendungen getaktet haben, haben Sie wahrscheinlich Date.now() verwendet, um einen DOMTimeStamp zurückzugeben. DOMTimeStamp gibt eine Ganzzahl in Millisekunden als Wert zurück. Um die für die hochauflösende Zeit erforderliche höhere Genauigkeit zu erreichen, wurde der neue Typ DOMHighResTimeStamp eingeführt. Dieser Typ ist ein Gleitkommawert, der auch die Zeit in Millisekunden zurückgibt. Da es sich jedoch um einen Gleitkommawert handelt, kann der Wert auch Bruchteile von Millisekunden darstellen und so eine Genauigkeit von einem Tausendstel einer Millisekunde liefern.

Benutzeroberfläche für das Nutzertiming

Da wir jetzt Zeitstempel mit hoher Auflösung haben, können wir über die Benutzeroberfläche Nutzerzeit Zeitinformationen abrufen.

Die Benutzerzeit-Benutzeroberfläche bietet Funktionen, mit denen wir Methoden an verschiedenen Stellen in unserer Anwendung aufrufen können. So erhalten wir eine Art Hansel-und-Gretel-Wegweiser, mit dem wir nachvollziehen können, wo die Zeit verbracht wird.

mark() verwenden

Die mark()-Methode ist das wichtigste Tool in unserem Toolkit zur Analyse der Zeitachsen. mark() speichert einen Zeitstempel für uns. Das Tolle an mark() ist, dass wir den Zeitstempel benennen können und die API den Namen und den Zeitstempel als einzelne Einheit speichert.

Wenn Sie mark() an verschiedenen Stellen in Ihrer Anwendung aufrufen, können Sie ermitteln, wie viel Zeit Sie für das Erreichen dieses „Markierungspunkts“ in Ihrer Webanwendung benötigt haben.

In der Spezifikation werden eine Reihe von Namen für Markierungen vorgeschlagen, die interessant sein könnten und ziemlich selbsterklärend sind, z. B. mark_fully_loaded,mark_fully_visible, mark_above_the_fold usw.

Mit dem folgenden Code können wir beispielsweise eine Markierung für den Zeitpunkt setzen, zu dem die Anwendung vollständig geladen ist:

window.performance.mark('mark_fully_loaded');

Wenn wir in unserer Webanwendung benannte Markierungen setzen, können wir eine ganze Reihe von Zeitdaten erfassen und in Ruhe analysieren, um herauszufinden, was die Anwendung wann tut.

Maße mit measure() berechnen

Nachdem Sie mehrere Markierungen gesetzt haben, möchten Sie wissen, wie viel Zeit zwischen ihnen vergangen ist. Dazu verwenden Sie die Methode measure().

Mit der measure()-Methode wird die verstrichene Zeit zwischen Markierungen berechnet. Außerdem lässt sich die Zeit zwischen einer Markierung und einem der bekannten Ereignisnamen auf der Benutzeroberfläche PerformanceTiming messen.

So können Sie beispielsweise die Zeit ermitteln, die vergeht, bis der DOM vollständig ist und der Anwendungsstatus vollständig geladen ist. Verwenden Sie dazu Code wie diesen:

window.performance.measure('measure_load_from_dom', 'domComplete', 'mark_fully_loaded');

Wenn Sie measure() aufrufen, wird das Ergebnis unabhängig von den von Ihnen festgelegten Markierungen gespeichert, sodass Sie es später abrufen können. Wenn Sie die Zeiten während der Ausführung der Anwendung speichern, bleibt die Anwendung reaktionsschnell. Sie können alle Daten nach Abschluss der Arbeit der Anwendung ausgeben, damit sie später analysiert werden können.

Markierungen mit clearMarks() verwerfen

Manchmal ist es hilfreich, eine Reihe von Markierungen zu entfernen, die Sie selbst erstellt haben. Beispielsweise können Sie Batchläufe für Ihre Webanwendung ausführen und möchten bei jedem Lauf neu beginnen.

Sie können alle von Ihnen erstellten Markierungen ganz einfach entfernen, indem Sie clearMarks() aufrufen.

Mit dem folgenden Beispielcode werden also alle vorhandenen Markierungen gelöscht, damit Sie bei Bedarf einen neuen Timing-Lauf einrichten können.

window.performance.clearMarks();

Es gibt natürlich auch Fälle, in denen Sie nicht alle Markierungen löschen möchten. Wenn Sie also bestimmte Markierungen entfernen möchten, können Sie einfach den Namen der Markierung übergeben, die Sie entfernen möchten. Beispiel:

window.performance.clearMarks('mark_fully_loaded');

wird die Markierung entfernt, die wir im ersten Beispiel gesetzt haben, während alle anderen Markierungen unverändert bleiben.

Möglicherweise möchten Sie auch alle von Ihnen vorgenommenen Maßnahmen entfernen. Dazu gibt es die entsprechende Methode clearMeasures(). Sie funktioniert genau wie clearMarks(), bezieht sich aber auf alle von Ihnen durchgeführten Messungen. Beispiel:

window.performance.clearMeasures('measure_load_from_dom');

entfernt die im obigen Beispiel measure() erstellte Maßnahme. Wenn Sie alle Messwerte entfernen möchten, funktioniert das genauso wie bei clearMarks(). Sie müssen dazu einfach clearMeasures() ohne Argumente aufrufen.

Timing-Daten abrufen

Es ist zwar gut, Markierungen zu setzen und Intervalle zu messen, aber irgendwann möchten Sie diese Zeitdaten für eine Analyse verwenden. Das ist auch ganz einfach. Sie müssen dazu nur die Benutzeroberfläche PerformanceTimeline verwenden.

Mit der Methode getEntriesByType() können wir beispielsweise alle Markierungen oder alle Messzeiträume als Liste abrufen, um sie zu durchsuchen und die Daten zu verarbeiten. Die Liste wird in chronologischer Reihenfolge zurückgegeben, sodass Sie die Markierungen in der Reihenfolge sehen, in der sie in Ihrer Webanwendung aufgerufen wurden.

Der Code unten:

var items = window.performance.getEntriesByType('mark');

gibt eine Liste aller Markierungen zurück, die in unserer Webanwendung aufgerufen wurden. Der Code:

var items = window.performance.getEntriesByType('measure');

gibt eine Liste aller Maßnahmen zurück, die wir ergriffen haben.

Sie können auch eine Liste der Einträge abrufen, indem Sie den bestimmten Namen verwenden, den Sie ihnen gegeben haben. Beispiel:

var items = window.performance.getEntriesByName('mark_fully_loaded');

würde eine Liste mit einem Element zurückgeben, das den Zeitstempel „mark_fully_loaded“ in der Property startTime enthält.

Timing einer XHR-Anfrage (Beispiel)

Nachdem wir uns ein gutes Bild von der User Timing API gemacht haben, können wir damit analysieren, wie lange alle XMLHttpRequests in unserer Webanwendung dauern.

Zuerst ändern wir alle send()-Anfragen, um einen Funktionsaufruf auszuführen, der die Markierungen einrichtet. Gleichzeitig ändern wir unsere Erfolgs-Callbacks durch einen Funktionsaufruf, der eine weitere Markierung setzt und dann ein Maß dafür generiert, wie lange die Anfrage gedauert hat.

Normalerweise sieht unsere XMLHttpRequest so aus:

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  do_something(e.responseText);
}
myReq.send();

In unserem Beispiel fügen wir einen globalen Zähler hinzu, um die Anzahl der Anfragen zu verfolgen und einen Messwert für jede Anfrage zu speichern. Der Code dazu sieht so aus:

var reqCnt = 0;

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  window.performance.mark('mark_end_xhr');
  reqCnt++;
  window.performance.measure('measure_xhr_' + reqCnt, 'mark_start_xhr', 'mark_end_xhr');
  do_something(e.responseText);
}
window.performance.mark('mark_start_xhr');
myReq.send();

Der Code oben generiert für jede gesendete XMLHttpRequest ein Messwert mit einem eindeutigen Namen. Wir gehen davon aus, dass die Anfragen nacheinander ausgeführt werden. Der Code für parallele Anfragen müsste etwas komplexer sein, um Anfragen zu verarbeiten, die nicht in der richtigen Reihenfolge zurückgegeben werden. Das überlassen wir dem Leser als Übung.

Nachdem die Webanwendung eine Reihe von Anfragen ausgeführt hat, können wir sie mit dem folgenden Code alle in die Konsole ausgeben:

var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
  var req = items[i];
  console.log('XHR ' + req.name + ' took ' + req.duration + 'ms');
}

Fazit

Die User Timing API bietet viele nützliche Tools, die Sie auf jeden Aspekt Ihrer Webanwendung anwenden können. Sie können die Hotspots in Ihrer Anwendung ganz einfach eingrenzen, indem Sie API-Aufrufe in Ihre Webanwendung einfügen und die generierten Zeitdaten nachbearbeiten, um ein klares Bild davon zu erhalten, wo die Zeit verbracht wird. Was ist aber, wenn Ihr Browser diese API nicht unterstützt? Kein Problem, hier finden Sie eine gute Polyfill, die die API sehr gut emuliert und auch mit webpagetest.org funktioniert. Worauf warten Sie also noch? Probieren Sie die User Timing API jetzt in Ihren Anwendungen aus. Sie werden herausfinden, wie Sie sie beschleunigen können, und Ihre Nutzer werden Ihnen dankbar sein, dass Sie die Nutzerfreundlichkeit so verbessert haben.