Nutzertiming-API

Informationen zu Ihrer Web-App

Alex Danilo

Leistungsstarke Webanwendungen sind entscheidend für eine gute Nutzererfahrung. Da Webanwendungen immer komplexer werden, ist es wichtig, die Auswirkungen auf die Leistung zu verstehen, um eine ansprechende Nutzererfahrung zu schaffen. In den letzten Jahren sind im Browser verschiedene APIs aufgetaucht, mit denen sich die Leistung des Netzwerks, die Ladezeiten usw. analysieren lassen. Diese liefern jedoch nicht unbedingt detaillierte Informationen mit ausreichender Flexibilität, um herauszufinden, was Ihre Anwendung verlangsamt. Geben Sie die User Timing API ein, die einen Mechanismus bietet, mit dem Sie Ihre Webanwendung instrumentieren können, um zu ermitteln, wo Ihre Anwendung Zeit verbringt. In diesem Artikel stellen wir die API vor und zeigen anhand von Beispielen, wie sie verwendet wird.

Ohne Messung keine Optimierung

Der erste Schritt zur Beschleunigung einer langsamen Webanwendung besteht darin, herauszufinden, wo Zeit verloren geht. Wenn Sie die zeitlichen Auswirkungen von JavaScript-Codebereichen messen, können Sie Hotspots identifizieren. Das ist der erste Schritt, um herauszufinden, wie Sie die Leistung verbessern können. Glücklicherweise bietet die User Timing API eine Möglichkeit, API-Aufrufe an verschiedenen Stellen Ihres JavaScript-Codes einzufügen und dann detaillierte Timing-Daten zu extrahieren, die Sie zur Optimierung verwenden können.

Hohe Auflösung und now()

Ein grundlegender Bestandteil der genauen Zeitmessung ist die Präzision. Früher haben wir die Zeit in Millisekunden gemessen. Das ist in Ordnung, aber für eine ruckelfreie Website mit 60 fps muss jeder Frame in 16 ms gezeichnet werden. Wenn Sie also nur eine Genauigkeit im Millisekundenbereich haben, fehlt die für eine gute Analyse erforderliche Präzision. High Resolution Time ist ein neuer Timing-Typ, der in moderne Browser integriert ist. High Resolution Time liefert uns Gleitkomma-Zeitstempel, die auf die Mikrosekunde genau sein können – tausendmal besser als zuvor.

Wenn Sie die aktuelle Zeit in Ihrer Webanwendung abrufen möchten, rufen Sie die Methode now() auf, die eine Erweiterung der Schnittstelle Performance ist. Der folgende Code zeigt, wie das geht:

var myTime = window.performance.now();

Es gibt eine weitere Schnittstelle namens PerformanceTiming, die verschiedene Zeiten im Zusammenhang mit dem Laden Ihrer Webanwendung liefert. Die Methode now() gibt die Zeit zurück, die seit dem Zeitpunkt navigationStart in PerformanceTiming vergangen ist.

Der Typ „DOMHighResTimeStamp“

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

Die Nutzertiming-Schnittstelle

Da wir jetzt Zeitstempel mit hoher Auflösung haben, können wir die User Timing-Schnittstelle verwenden, um Zeitinformationen abzurufen.

Die Nutzertiming-Schnittstelle bietet Funktionen, mit denen wir Methoden an verschiedenen Stellen in unserer Anwendung aufrufen können. Diese können einen Pfad im Stil von Hänsel und Gretel bereitstellen, mit dem wir nachverfolgen können, wo die Zeit verbracht wird.

mark() verwenden

Die Methode mark() ist das wichtigste Tool in unserem Toolkit für die Timing-Analyse. mark() speichert einen Zeitstempel für uns. Das Tolle an mark() ist, dass wir dem Zeitstempel einen Namen geben können und die API sich den Namen und den Zeitstempel als eine Einheit merkt.

Wenn Sie mark() an verschiedenen Stellen in Ihrer Anwendung aufrufen, können Sie herausfinden, wie lange es gedauert hat, bis Sie diese „Markierung“ in Ihrer Webanwendung erreicht haben.

In der Spezifikation werden einige Namen für Markierungen vorgeschlagen, die interessant und selbsterklärend sind, z. B. mark_fully_loaded, mark_fully_visible, mark_above_the_fold usw.

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

window.performance.mark('mark_fully_loaded');

Durch das Festlegen benannter Markierungen in unserer Webanwendung können wir eine ganze Reihe von Zeitdaten erfassen und sie in Ruhe analysieren, um herauszufinden, was die Anwendung wann tut.

Messungen mit measure() berechnen

Nachdem du mehrere Zeitmarken gesetzt hast, kannst du die verstrichene Zeit zwischen ihnen ermitteln. Dazu verwenden Sie die Methode measure().

Mit der Methode measure() wird die verstrichene Zeit zwischen Markierungen berechnet. Außerdem kann die Zeit zwischen Ihrer Markierung und einem der bekannten Ereignisnamen in der PerformanceTiming-Schnittstelle gemessen werden.

Sie können beispielsweise die Zeit von der Fertigstellung des DOM bis zum vollständigen Laden des Anwendungsstatus mit folgendem Code ermitteln:

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 reaktionsfähig. 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 nützlich, eine Reihe von Markierungen, die Sie eingerichtet haben, zu entfernen. Beispielsweise können Sie Batchausführungen für Ihre Webanwendung ausführen und möchten daher bei jeder Ausführung neu beginnen.

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

Der folgende Beispielcode würde also alle vorhandenen Markierungen löschen, sodass Sie bei Bedarf einen neuen Zeitmessungslauf einrichten können.

window.performance.clearMarks();

Natürlich gibt es auch Szenarien, 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');

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

Möglicherweise möchten Sie auch alle von Ihnen erstellten Messwerte entfernen. Dazu gibt es die Methode clearMeasures(). Sie funktioniert genau wie clearMarks(), wird aber auf alle von Ihnen vorgenommenen Messungen angewendet. Beispiel:

window.performance.clearMeasures('measure_load_from_dom');

wird die im obigen measure()-Beispiel erstellte Measure entfernt. Wenn Sie alle Messwerte entfernen möchten, funktioniert das genauso wie clearMarks() – Sie rufen einfach clearMeasures() ohne Argumente auf.

Timing-Daten abrufen

Es ist schön und gut, Markierungen zu setzen und Intervalle zu messen, aber irgendwann möchten Sie auf diese Timing-Daten zugreifen, um eine Analyse durchzuführen. Das ist auch ganz einfach. Sie müssen nur die PerformanceTimeline-Schnittstelle verwenden.

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

Der folgende Code:

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

gibt eine Liste aller Markierungen zurück, die in unserer Webanwendung erreicht wurden, während der Code:

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

gibt eine Liste aller von uns erstellten Measures zurück.

Sie können auch eine Liste von Einträgen abrufen, indem Sie den spezifischen 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 nun ein Bild von der User Timing API gemacht haben, können wir sie verwenden, um zu analysieren, wie lange alle XMLHttpRequests in unserer Webanwendung dauern.

Zuerst ändern wir alle send()-Anfragen so, dass ein Funktionsaufruf ausgegeben wird, der die Markierungen einrichtet. Gleichzeitig ändern wir die Erfolgs-Callbacks mit einem Funktionsaufruf, der eine weitere Markierung setzt und dann eine Messung der Dauer der Anfrage generiert.

Normalerweise würde unser XMLHttpRequest so aussehen:

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 erfassen und einen Messwert für jede Anfrage zu speichern. Der Code dafür 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 obige Code generiert für jeden gesendeten XMLHttpRequest eine Messung mit einem eindeutigen Namenswert. 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 alle mit dem folgenden Code in der 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 Ihrer Webanwendung verteilen und die generierten Zeitdaten nachbearbeiten, um ein klares Bild davon zu erhalten, wo Zeit verbraucht wird. Was aber, wenn Ihr Browser diese API nicht unterstützt? Kein Problem, hier finden Sie ein hervorragendes Polyfill, das die API sehr gut emuliert und auch mit webpagetest.org gut 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 es Ihnen danken, dass Sie ihre Erfahrung so viel besser gemacht haben.