Nutzertiming-API

Informationen zur Webanwendung

Alex Danilo

Hochleistungsfähige Webanwendungen sind für eine hervorragende Nutzererfahrung entscheidend. Webanwendungen werden immer komplexer, deshalb ist es für eine überzeugende Nutzererfahrung wichtig, die Auswirkungen auf die Leistung 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 zur Beschleunigung einer langsamen Webanwendung besteht darin, herauszufinden, wo die Zeit verbringt. Das Messen der zeitlichen Auswirkungen von JavaScript-Codebereichen ist die ideale Methode, um Hotspots zu identifizieren. Dies ist der erste Schritt zur Leistungsverbesserung. 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 für die hohe Auflösung und now()

Präzision ist ein wesentlicher Bestandteil einer genauen Zeitmessung. Früher basierten wir auf einer Messung im Millisekundenbereich, was in Ordnung ist, aber das Erstellen einer 60-fps-Website ohne Verzögerung bedeutet, dass jeder Frame in 16 ms gezeichnet werden muss. Bei einer Genauigkeit im Millisekundenbereich fehlt es also an Präzision, die für eine gute Analyse erforderlich ist. Hier kommt Zeit für hohe Auflösung, ein neuer Timing-Typ, der in modernen Browsern bereits 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. Sie bildet eine Erweiterung der Schnittstelle Leistung. Im folgenden Code wird gezeigt, wie das geht:

var myTime = window.performance.now();

Eine weitere Schnittstelle namens PerformanceTiming bietet eine Reihe von unterschiedlichen Zeitpunkten in Abhängigkeit davon, wie Ihre Webanwendung geladen wird. Die Methode now() gibt die Zeit seit dem Zeitpunkt navigationStart in PerformanceTiming zurück.

Der Typ „DOMHighResTimeStamp“

Wenn Sie das Timing von Webanwendungen in der Vergangenheit messen möchten, verwenden Sie beispielsweise Date.now(). Dieser gibt DOMTimeStamp zurück. 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 für uns einen Zeitstempel. 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 wird eine Reihe von Namen vorgeschlagen, die interessant und selbsterklärend sein könnten, z. B. mark_fully_loaded, mark_fully_visible oder mark_above_the_fold.

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 sie später abrufen können. Da die Zeit während der Ausführung Ihrer Anwendung gespeichert wird, bleibt die Anwendung responsiv und Sie können alle Daten ausfindig machen, nachdem die Anwendung einige Arbeit beendet hat, 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.

Der Beispielcode unten würde also alle vorhandenen Markierungen außer Acht lassen, sodass Sie bei Bedarf erneut einen Zeitlauf 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.

Wenn Sie alle von Ihnen durchgeführten Messungen löschen möchten, gibt es dafür die entsprechende Methode clearMeasures(). Sie funktioniert genau wie clearMarks(), aber an allen von Ihnen durchgeführten Messungen. Zum Beispiel der Code:

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. Du musst 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. Das Schöne ist, dass die Liste in chronologischer Reihenfolge zurückgegeben wird, sodass Sie die Markierungen in der Reihenfolge sehen können, in der sie in Ihrer Webanwendung getroffen wurden.

Der folgende Code:

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. Zum Beispiel würde der Code:

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 würde die XMLHttpRequest-Anfrage in etwa wie folgt 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 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. Wir belassen das als Übung für den Leser.

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 aber, wenn Ihr Browser diese API nicht unterstützt? Kein Problem, hier findest du ein sehr gutes Polyfill, das 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 es Ihnen danken, dass Sie ihre Erfahrung so viel verbessert haben.