API User Timing

Comprendre votre application Web

Alex Danilo

Les applications Web hautes performances sont essentielles pour offrir une expérience utilisateur de qualité. À mesure que les applications Web deviennent 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 ont été ajoutées au navigateur pour aider à analyser les performances du réseau, les temps de chargement, etc., mais elles ne fournissent pas nécessairement des détails précis avec une flexibilité suffisante pour identifier ce qui ralentit votre application. Entrez l'API User Timing, qui fournit un mécanisme que vous pouvez utiliser pour instrumenter votre application Web afin d'identifier où elle passe son temps. Dans cet article, nous allons présenter l'API et des exemples d'utilisation.

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ù le temps est dépensé. Mesurer l'impact temporel des zones de code JavaScript est le moyen idéal d'identifier les points chauds, ce qui constitue la première étape pour trouver comment améliorer les performances. Heureusement, l'API User Timing vous permet d'insérer des appels d'API dans différentes parties de votre code JavaScript, puis d'extraire des données temporelles détaillées qui peuvent vous aider à optimiser votre code.

Durée de la haute résolution et now()

La précision est un élément essentiel de la mesure précise du temps. Auparavant, nous utilisions des mesures en millisecondes pour le timing, ce qui est acceptable. Toutefois, pour créer un site à 60 ips sans à-coups, chaque image doit être dessinée en 16 ms. Par conséquent, lorsque vous n'avez qu'une précision de milliseconde, vous n'avez pas la précision nécessaire pour une bonne analyse. Saisissez High Resolution Time (Temps haute résolution), un nouveau type de synchronisation intégré aux navigateurs modernes. La date et l'heure haute résolution nous fournissent des codes temporels à virgule flottante dont la précision peut atteindre la microseconde, soit mille fois mieux qu'auparavant.

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

var myTime = window.performance.now();

Une autre interface appelée PerformanceTiming fournit un certain nombre de temps différents liés à la façon dont votre application Web est chargée. La méthode now() renvoie le temps écoulé depuis l'heure navigationStart dans PerformanceTiming.

Type DOMHighResTimeStamp

Auparavant, pour chronométrer des applications Web, vous utilisiez Date.now(), qui renvoie un DOMTimeStamp. DOMTimeStamp renvoie un nombre entier de millisecondes comme valeur. Pour fournir la précision plus élevée requise pour l'heure haute résolution, un nouveau type appelé DOMHighResTimeStamp a été introduit. Il s'agit d'une valeur à virgule flottante qui renvoie également l'heure en millisecondes. Toutefois, comme il s'agit d'un nombre à virgule flottante, la valeur peut représenter des millisecondes fractionnaires et peut donc fournir une précision au millième de milliseconde.

Interface User Timing

Maintenant que nous disposons de codes temporels haute résolution, utilisons l'interface User Timing (Temps utilisateur) pour extraire des informations temporelles.

L'interface User Timing 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 suivre le temps passé.

Utiliser mark()

La méthode mark() est l'outil principal de notre boîte à outils d'analyse temporelle. mark() stocke un code temporel pour nous. mark() est très utile, car nous pouvons nommer le code temporel. L'API mémorisera le nom et le code temporel en tant qu'unité unique.

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

La spécification indique un certain nombre de noms suggérés pour les marques qui peuvent être intéressants et assez explicites, tels que mark_fully_loaded, mark_fully_visible,mark_above_the_fold, etc.

Par exemple, nous pouvons définir une marque pour le moment où l'application est entièrement chargée à l'aide du code suivant:

window.performance.mark('mark_fully_loaded');

En définissant des repères nommés dans notre application Web, nous pouvons collecter un grand nombre de données temporelles et les analyser à notre guise pour déterminer ce que l'application fait et quand.

Calculer des mesures avec measure()

Une fois que vous avez défini plusieurs repères temporels, vous devez déterminer la durée écoulée entre eux. 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 repère et l'un des noms d'événement bien connus dans l'interface PerformanceTiming.

Par exemple, vous pouvez calculer le temps écoulé entre la finalisation du DOM et le chargement complet de l'état de votre application à l'aide d'un code tel que:

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

Lorsque vous appelez measure(), il stocke le résultat indépendamment des repères que vous définissez, afin que vous puissiez les récupérer plus tard. En stockant les temps d'inactivité pendant l'exécution de votre application, celle-ci reste réactive. Vous pouvez ensuite vider toutes les données une fois que votre application a terminé une tâche afin qu'elles puissent être analysées plus tard.

Supprimer des repères avec clearMarks()

Il est parfois utile de pouvoir supprimer un grand nombre de repères que vous avez configurés. Par exemple, vous pouvez effectuer des traitements par lot sur votre application Web et vous souhaitez donc repartir à zéro à chaque exécution.

Il est facile de supprimer toutes les marques que vous avez configurées en appelant clearMarks().

L'exemple de code ci-dessous supprimerait toutes les marques existantes afin que vous puissiez configurer à nouveau une analyse temporelle si vous le souhaitez.

window.performance.clearMarks();

Bien sûr, il peut arriver que vous ne souhaitiez pas effacer toutes vos marques. Si vous souhaitez supprimer des marques spécifiques, vous pouvez simplement transmettre le nom de la marque que vous souhaitez supprimer. Par exemple, le code ci-dessous:

window.performance.clearMarks('mark_fully_loaded');

supprime la marque que nous avons définie dans le premier exemple, tout en laissant les autres marques inchangées.

Vous pouvez également supprimer toutes les mesures que vous avez effectuées. Pour ce faire, utilisez la méthode clearMeasures(). Il fonctionne exactement de la même manière que clearMarks(), mais sur toutes les mesures que vous avez effectuées. Par exemple, le code suivant:

window.performance.clearMeasures('measure_load_from_dom');

supprimera la mesure que nous avons effectuée dans l'exemple measure() ci-dessus. Si vous souhaitez supprimer toutes les mesures, cela fonctionne exactement comme clearMarks(), c'est-à-dire que vous appelez simplement clearMeasures() sans arguments.

Extraire les données de synchronisation

Il est bien de définir des repères et de mesurer des intervalles, mais à un moment donné, vous devrez accéder à ces données temporelles pour effectuer des analyses. C'est aussi très simple. Il vous suffit d'utiliser l'interface PerformanceTimeline.

Par exemple, la méthode getEntriesByType() nous permet d'obtenir toutes nos marques de temps ou toutes nos mesures sous forme de liste afin de pouvoir les itérer et d'analyser les données. L'avantage est que la liste est renvoyée dans l'ordre chronologique. Vous pouvez ainsi voir les repères dans l'ordre dans lequel ils ont été atteints dans votre application Web.

Le code ci-dessous:

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

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

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

renvoie la liste de toutes les mesures que nous avons effectuées.

Vous pouvez également obtenir une liste d'entrées à l'aide du nom spécifique que vous leur avez attribué. Par exemple, le code:

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

renvoie une liste contenant un seul élément contenant le code temporel "mark_fully_loaded" 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 nos XMLHttpRequests dans notre application Web.

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

Normalement, notre XMLHttpRequest se présente comme suit:

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

Pour 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. Le code à utiliser 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 supposons que les requêtes s'exécutent de manière séquentielle. Le code des requêtes parallèles devrait être un peu plus complexe pour gérer les requêtes renvoyées dans le désordre. Nous laissons cela au lecteur comme exercice.

Une fois que l'application Web a effectué un certain nombre de requêtes, nous pouvons toutes les vider 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 fournit de nombreux outils utiles à appliquer à n'importe quel aspect de votre application Web. Vous pouvez facilement identifier les points chauds de votre application en insérant des appels d'API dans votre application Web et en post-traitant les données temporelles générées pour obtenir une image claire de l'utilisation du temps. Mais que se passe-t-il si votre navigateur n'est pas compatible avec cette API ? Ne vous inquiétez pas, vous trouverez ici un excellent polyfill qui émule très bien l'API et qui fonctionne également avec webpagetest.org. Alors, qu'attendez-vous ? Essayez l'API User Timing dans vos applications dès maintenant. Vous découvrirez comment les accélérer, et vos utilisateurs vous remercieront d'avoir amélioré leur expérience.