API User Timing

Comprendere la tua applicazione web

Alex Danilo

Le applicazioni web ad alte prestazioni sono fondamentali per un'ottima esperienza utente. Man mano che le applicazioni web diventano sempre più complesse, comprendere l'impatto sulle prestazioni è fondamentale per creare un'esperienza accattivante. Nel corso degli ultimi anni, sono apparse una serie di API diverse nel browser per aiutare ad analizzare le prestazioni della rete, i tempi di caricamento e così via, ma queste non sempre forniscono dettagli granulari con flessibilità sufficiente per trovare ciò che rallenta l'applicazione. Inserisci l'API User Timing, che fornisce un meccanismo che puoi utilizzare per consentire alla tua applicazione web di identificare dove trascorre il tempo. Questo articolo illustra l'API e gli esempi di utilizzo.

Non puoi ottimizzare ciò che non puoi misurare

Il primo passo per accelerare un'applicazione web lenta consiste nel capire dove viene dedicato il tempo. La misurazione dell'impatto temporale delle aree di codice JavaScript è il modo ideale per identificare i punti critici, che è il primo passo per capire come migliorare le prestazioni. Fortunatamente l'API User Timing consente di inserire chiamate API in diverse parti del codice JavaScript e di estrarre dati di tempo dettagliati che possono essere utilizzati per l'ottimizzazione.

Tempo in alta risoluzione e now()

Una parte fondamentale della precisione della misurazione del tempo è la precisione. In passato avevamo un tempo di misurazione basato su una misurazione in millisecondi, che va bene, ma creare un sito a 60 FPS senza jank significa che ogni frame doveva essere disegnato in 16 ms. Quindi, quando si dispone di una precisione di solo un millisecondo, manca la precisione necessaria per una buona analisi. Inserisci Tempo ad alta risoluzione, un nuovo tipo di temporizzazione integrato nei browser moderni. L'opzione Tempo ad alta risoluzione ci fornisce indicatori temporali in virgola mobile che possono essere accurati con una risoluzione in microsecondi, mille volte migliori rispetto a prima.

Per ottenere l'ora corrente nell'applicazione web, chiama il metodo now() che forma un'estensione dell'interfaccia Rendimento. Il seguente codice mostra come fare:

var myTime = window.performance.now();

Esiste un'altra interfaccia chiamata PerformanceTiming, che mostra una serie di orari diversi relativi al modo in cui viene caricata l'applicazione web. Il metodo now() restituisce il tempo trascorso dal momento in cui si è verificato il tempo navigationStart in PerformanceTiming.

Il tipo DOMHighResTimeStamp

Quando provi a cronometrare le applicazioni web in passato, potresti utilizzare qualcosa come Date.now(), che restituisce un DOMTimeStamp. DOMTimeStamp restituisce un numero intero di millisecondi come valore. Per garantire la maggiore precisione necessaria per i tempi di alta risoluzione, è stato introdotto un nuovo tipo denominato DOMHighResTimeStamp. Questo tipo è un valore in virgola mobile che restituisce anche il tempo in millisecondi. Tuttavia, poiché è in virgola mobile, il valore può rappresentare una frazione di millisecondi, quindi può fornire un'accuratezza di un millesimo di millisecondo.

Interfaccia dei tempi utente

Ora che abbiamo i timestamp ad alta risoluzione, utilizziamo l'interfaccia Tempi utente per estrarre le informazioni sulle tempistiche.

L'interfaccia dei tempi utente fornisce funzioni che ci permettono di chiamare metodi in punti diversi della nostra applicazione che possono fornire una traccia di breadcrumb in stile Hansel e Gretel per consentirci di tenere traccia di dove viene trascorso il tempo.

In uso: mark()

Il metodo mark() è lo strumento principale del nostro toolkit di analisi delle tempistiche. mark() memorizza un timestamp. L'aspetto molto utile di mark() è che possiamo assegnare un nome al timestamp e l'API ricorderà il nome e il timestamp come una singola unità.

Chiamare mark() in vari punti dell'applicazione ti consente di capire quanto tempo è stato necessario selezionare il "segno" nell'applicazione web.

La specifica richiama una serie di nomi suggeriti per i segni che potrebbero essere interessanti e che sono abbastanza chiari, ad esempio mark_fully_loaded, mark_fully_visible,mark_above_the_fold e così via.

Ad esempio, utilizzando il seguente codice potremmo impostare un contrassegno per il caricamento completo dell'applicazione:

window.performance.mark('mark_fully_loaded');

Impostando i contrassegni con nome nella nostra applicazione web, possiamo raccogliere moltissimi dati di tempo e analizzarli quando vogliamo per capire cosa sta facendo l'applicazione e quando.

Calcolo delle misurazioni con measure() in corso...

Una volta impostati alcuni indicatori di temporizzazione, dovrai scoprire il tempo che intercorre tra loro. Per farlo, puoi usare il metodo measure().

Il metodo measure() calcola il tempo trascorso tra i segni e può anche misurare il tempo tra il tuo contrassegno e uno dei nomi di eventi noti nell'interfaccia PerformanceTiming.

Ad esempio, puoi calcolare il tempo che intercorre tra il completamento del DOM e il completamento del caricamento dello stato dell'applicazione utilizzando un codice come:

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

Quando chiami measure(), il risultato viene archiviato indipendentemente dai contrassegni impostati, così puoi recuperarlo in un secondo momento. Archiviando i tempi di assenza durante l'esecuzione dell'applicazione, quest'ultima rimane reattiva e puoi eseguire il dump di tutti i dati una volta che l'applicazione ha terminato di lavorare, in modo che possa essere analizzata in un secondo momento.

Eliminazione dei segni con clearMarks()

A volte è utile riuscire a eliminare una serie di segni che si sono creati. Ad esempio, puoi eseguire esecuzioni batch sulla tua applicazione web e ricominciare da capo ogni esecuzione.

Rimuovere tutti i segni che hai impostato è molto semplice chiamando il numero clearMarks().

Di conseguenza, il codice di esempio riportato di seguito eliminerà tutti i segni esistenti esistenti, in modo che tu possa impostare di nuovo una tempistica di esecuzione, se vuoi.

window.performance.clearMarks();

Naturalmente, in alcuni casi potresti non voler cancellare tutti i segni. Quindi, se vuoi eliminare segni specifici, puoi semplicemente passare il nome del segno che vuoi rimuovere. Ad esempio, il codice riportato di seguito:

window.peformance.clearMarks('mark_fully_loaded');

si sbarazza del contrassegno che abbiamo impostato nel primo esempio, lasciando invariati gli altri segni impostati.

Potresti voler eliminare tutte le misure che hai apportato ed esiste un metodo corrispondente per farlo: clearMeasures(). Funziona esattamente come clearMarks(), ma funziona sulle misurazioni che hai effettuato. Ad esempio, il codice:

window.performance.clearMeasures('measure_load_from_dom');

rimuoverà la misura specificata nell'esempio measure() sopra riportato. Se vuoi rimuovere tutte le misure, funziona come clearMarks(), in quanto basta chiamare clearMeasures() senza argomenti.

Recupero dei dati di temporizzazione

Impostare dei segni e misurare gli intervalli va bene, ma a un certo punto vuoi ottenere dati in quel momento per eseguire qualche analisi. Anche questo è molto semplice: devi solo utilizzare l'interfaccia di PerformanceTimeline.

Ad esempio, il metodo getEntriesByType() ci consente di ottenere tutti i tempi di valutazione o i timeout di misura sotto forma di elenco per eseguire l'iterazione e sintetizzare i dati. La cosa positiva è che l'elenco viene restituito in ordine cronologico, quindi puoi vedere i segni nell'ordine in cui sono stati raggiunti nella tua applicazione web.

Il seguente codice:

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

ci restituisce un elenco di tutti i marchi che sono stati trovati nella nostra applicazione web, mentre il codice:

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

restituisce un elenco di tutte le misure che abbiamo adottato.

Puoi anche recuperare un elenco di voci utilizzando il nome specifico assegnato. Ad esempio, il codice:

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

ci restituirebbe un elenco con una voce contenente il timestamp "mark_full_loading" nella proprietà startTime.

Tempistiche di una richiesta XHR (esempio)

Ora che abbiamo una buona immagine dell'API User Timing, possiamo utilizzarla per analizzare tutti i nostri XMLHttpRequests nella nostra applicazione web.

Innanzitutto modificheremo tutte le richieste di send() per emettere una chiamata di funzione che imposti i segni e allo stesso tempo modificheremo i callback di successo con una chiamata di funzione che imposti un altro contrassegno e generi una misura del tempo richiesto dalla richiesta.

Quindi normalmente il nostro XMLHttpRequest avrebbe un aspetto simile a questo:

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

Nel nostro esempio, aggiungeremo un contatore globale per tenere traccia del numero di richieste e anche per utilizzarlo per memorizzare una misura per ogni richiesta effettuata. Il codice per eseguire questa operazione ha il seguente aspetto:

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

Il codice riportato sopra genera una misura con un valore del nome univoco per ogni XMLHttpRequest inviato. Presumiamo che le richieste vengano eseguite in sequenza: il codice per le richieste parallele dovrebbe essere un po' più complesso per gestire le richieste che ritornano nell'ordine sbagliato. Lo lasceremo come esercizio per il lettore.

Una volta che l'applicazione web ha inviato diverse richieste, possiamo scaricarle tutte nella console utilizzando il codice seguente:

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

Conclusione

L'API User Timing ti offre molti ottimi strumenti per applicare qualsiasi aspetto della tua applicazione web. È possibile ottenere facilmente gli hotspot nella tua applicazione distribuendo chiamate API nell'applicazione web e post-elaborando i dati di tempo generati per creare un quadro chiaro di dove viene trascorso il tempo. Ma cosa succede se il tuo browser non supporta questa API? Nessun problema, puoi trovare un ottimo polyfill qui che emula molto bene l'API e funzioni bene anche con webpagetest.org. Cosa stai aspettando? Prova subito l'API User Timing sulle tue applicazioni, scoprirai come velocizzarle e i tuoi utenti ti ringrazieranno per aver reso la loro esperienza molto migliore.