API User Timing

Comprendre votre application Web

Alex Danilo

Les applications Web hautes performances sont essentielles à une excellente expérience utilisateur. Les applications Web étant de plus en plus complexes, il est essentiel de comprendre l'impact sur les performances pour créer une expérience attrayante. Au cours des dernières années, un certain nombre d'API différentes sont apparues dans le navigateur pour permettre d'analyser les performances du réseau, les temps de chargement, etc. Mais elles ne permettent pas nécessairement d'obtenir des détails précis avec suffisamment de flexibilité pour identifier ce qui ralentit votre application. L'API User Timing fournit un mécanisme permettant d'instrumenter votre application Web afin d'identifier les tâches effectuées par celle-ci. Dans cet article, nous allons vous présenter l'API et vous expliquer comment l'utiliser.

Vous ne pouvez pas optimiser ce que vous ne pouvez pas mesurer

La première étape pour accélérer une application Web lente consiste à déterminer où se passe le temps. Mesurer l'impact temporel des zones du code JavaScript constitue la méthode idéale pour identifier les zones cliquables, ce qui constitue la première étape pour améliorer les performances. Heureusement, l'API User Timing permet d'insérer des appels d'API à différentes parties de votre code JavaScript, puis d'extraire des données temporelles détaillées qui peuvent vous aider à optimiser votre site.

Délai haute résolution et now()

La précision est un élément fondamental pour mesurer précisément le temps. Auparavant, nous utilisions le temps nécessaire pour mesurer la milliseconde, ce qui est acceptable. Toutefois, si vous souhaitez créer un site à 60 FPS sans à-coups, chaque image devait être dessinée en 16 ms. Par conséquent, si la précision est de l'ordre de la milliseconde, elle n'est pas assez précise pour permettre une bonne analyse. Indiquez le temps haute résolution, un nouveau type de codes temporels intégré aux navigateurs récents. Le temps haute résolution fournit des horodatages à virgule flottante avec une précision de l'ordre de la microseconde, soit mille fois meilleure qu'auparavant.

Pour obtenir l'heure actuelle dans votre application Web, appelez la méthode now() qui constitue une extension de l'interface Performance. Le code suivant montre comment procéder:

var myTime = window.performance.now();

Il existe une autre interface appelée PerformanceTiming, qui fournit un certain nombre d'heures différentes liées au chargement de votre application Web. La méthode now() renvoie le temps écoulé à partir du moment où l'heure navigationStart dans PerformanceTiming s'est produite.

Type DOMHighResTimeStamp

Pour chronométrer des applications Web par le passé, utilisez par exemple Date.now(), qui renvoie un élément DOMTimeStamp. DOMTimeStamp renvoie un nombre entier de millisecondes comme valeur. Un nouveau type appelé DOMHighResTimeStamp a été introduit afin de fournir la plus grande précision nécessaire pour le temps haute résolution. Il s'agit d'une valeur à virgule flottante qui renvoie également la durée en millisecondes. Toutefois, comme il s'agit d'une valeur à virgule flottante, la valeur peut représenter des millisecondes, ce qui peut donner une précision d'un millième de milliseconde.

Interface de gestion du temps utilisateur

Maintenant que nous disposons d'horodatages en haute résolution, nous allons utiliser l'interface Temps utilisateur pour extraire les informations temporelles.

L'interface de temps utilisateur fournit des fonctions qui nous permettent d'appeler des méthodes à différents endroits de notre application. Elles peuvent fournir un fil d'Ariane de style Hansel et Gretel pour nous permettre de savoir où le temps est passé.

Utiliser mark()

La méthode mark() est le principal outil de notre boîte à outils d'analyse du temps. mark() stocke un horodatage pour nous. Ce qui est très utile avec mark(), c'est que nous pouvons nommer l'horodatage, et l'API mémorisera le nom et l'horodatage comme une seule unité.

Appeler mark() à différents endroits de votre application vous permet de déterminer le temps qu'il vous a fallu pour atteindre ce "marque" dans votre application Web.

La spécification propose un certain nombre de noms suggérés pour les marques susceptibles d'être intéressantes et qui se passent d'explication, telles que mark_fully_loaded, mark_fully_visible,mark_above_the_fold, etc.

Par exemple, nous pouvons définir une marque pour le chargement complet de l'application à l'aide du code suivant:

window.performance.mark('mark_fully_loaded');

En définissant des marques nommées dans notre application Web, nous pouvons collecter un grand nombre de données temporelles et les analyser à notre rythme pour déterminer ce que fait l'application et à quel moment.

Calcul des mesures avec measure()

Une fois que vous avez défini un certain nombre de marques de temps, vous devez connaître le temps écoulé entre elles. Pour ce faire, utilisez la méthode measure().

La méthode measure() calcule le temps écoulé entre les repères, et peut également mesurer le temps écoulé entre votre marque et tout nom d'événement connu dans l'interface PerformanceTiming.

Par exemple, vous pouvez déterminer le temps qui s'écoule entre la fin du DOM et le chargement complet de l'état de votre application à l'aide du code suivant:

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

Lorsque vous appelez measure(), le résultat est stocké indépendamment des marques que vous avez définies afin que vous puissiez les récupérer ultérieurement. En stockant les périodes d'absence pendant l'exécution de votre application, celle-ci reste réactive. De plus, vous pouvez vider toutes les données une fois le travail terminé pour que l'application puisse être analysée ultérieurement.

Supprimer des marques avec clearMarks()

Il est parfois utile de pouvoir se débarrasser d'un grand nombre de marques que vous avez configurées. Par exemple, si vous effectuez des exécutions par lot sur votre application Web, vous devez recommencer à zéro à chaque exécution.

Vous pouvez facilement supprimer toutes les marques que vous avez configurées en appelant clearMarks().

Ainsi, l'exemple de code ci-dessous supprimerait toutes les marques existantes, afin que vous puissiez reconfigurer une exécution temporelle si vous le souhaitez.

window.performance.clearMarks();

Bien entendu, dans certains cas, vous ne souhaiterez peut-être pas effacer toutes vos marques. Ainsi, si vous voulez supprimer des marques spécifiques, il vous suffit de transmettre leur nom. Par exemple, le code ci-dessous:

window.peformance.clearMarks('mark_fully_loaded');

permet de se débarrasser de la marque définie dans le premier exemple tout en laissant les autres marques définies telles quelles.

Vous pouvez également vous débarrasser de toutes les mesures que vous avez créées. Pour ce faire, il existe une méthode correspondante appelée clearMeasures(). Elle fonctionne exactement de la même manière que clearMarks(), mais en fonction des mesures que vous avez effectuées. Par exemple, le code:

window.performance.clearMeasures('measure_load_from_dom');

supprimera la mesure que nous avons réalisée dans l'exemple measure() ci-dessus. Si vous souhaitez supprimer toutes les mesures, cela fonctionne exactement comme clearMarks(), en ce sens que vous appelez simplement clearMeasures() sans arguments.

Extraire les données de délai

C'est bien de définir des marques et de mesurer des intervalles, mais à un moment donné, vous souhaitez obtenir des données à ce moment-là pour effectuer une analyse. C'est aussi très simple : il vous suffit d'utiliser l'interface PerformanceTimeline.

Par exemple, la méthode getEntriesByType() nous permet d'obtenir tous les temps de marquage, ou toutes les mesures et les délais sous forme de liste, afin que nous puissions itérer et condenser les données. L'avantage, c'est que la liste est renvoyée dans l'ordre chronologique, ce qui vous permet de voir les marques dans l'ordre dans lequel elles ont été appelées dans votre application Web.

Le code ci-dessous:

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

nous renvoie la liste de toutes les marques qui ont été appelées dans notre application Web, tandis que le code:

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

renvoie une liste de toutes les mesures que nous avons prises.

Vous pouvez également récupérer la liste des entrées en utilisant le nom spécifique que vous leur avez attribué. Ainsi, par exemple, le code:

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

renvoie une liste contenant un seul élément, avec l'horodatage "mark_full_loading" dans la propriété startTime.

Chronométrer une requête XHR (exemple)

Maintenant que nous avons une bonne idée de l'API User Timing, nous pouvons l'utiliser pour analyser la durée de toutes les requêtes XMLHttpRequests de notre application Web.

Tout d'abord, nous allons modifier toutes nos requêtes send() pour émettre un appel de fonction qui configure les marques, et en même temps modifier nos rappels de réussite avec un appel de fonction qui définit une autre marque, puis génère une mesure de la durée de la requête.

Normalement, notre requête XMLHttpRequest devrait ressembler à ceci:

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

Dans notre exemple, nous allons ajouter un compteur global pour suivre le nombre de requêtes et l'utiliser pour stocker une mesure pour chaque requête effectuée. Pour ce faire, le code se présente comme suit:

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();

Le code ci-dessus génère une mesure avec une valeur de nom unique pour chaque XMLHttpRequest que nous envoyons. Nous partons du principe que les requêtes s'exécutent dans l'ordre. Le code des requêtes parallèles devrait être un peu plus complexe pour traiter les requêtes qui sont renvoyées dans le désordre. Nous allons laisser cela à un exercice pour le lecteur.

Une fois que l'application Web a envoyé plusieurs requêtes, nous pouvons toutes les envoyer dans la console à l'aide du code ci-dessous:

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');
}

Conclusion

L'API User Timing vous offre de nombreux outils efficaces à appliquer à n'importe quel aspect de votre application Web. Pour réduire les points d'accès dans votre application, vous pouvez facilement répartir les appels d'API sur l'ensemble de votre application Web et post-traiter les données temporelles générées afin de créer une idée précise de l'impact du temps passé. Que faire si votre navigateur n'est pas compatible avec cette API ? Aucun problème. Vous trouverez ici un polyfill qui émule parfaitement l'API et fonctionne parfaitement avec webpagetest.org. Alors, qu'attendez-vous ? Essayez l'API User Timing dans vos applications dès maintenant. Vous découvrirez comment les rendre plus rapides, et vos utilisateurs vous remercieront d'avoir amélioré leur expérience.