API User Timing

Informazioni sull'app web

Alex Danilo

Le applicazioni web ad alte prestazioni sono fondamentali per un'esperienza utente ottimale. Man mano che le applicazioni web diventano sempre più complesse, comprendere l'impatto sulle prestazioni è fondamentale per creare un'esperienza accattivante. Negli ultimi anni, nel browser sono apparse diverse API per aiutare ad analizzare le prestazioni della rete, i tempi di caricamento e così via, ma queste non forniscono necessariamente dettagli granulari con una flessibilità sufficiente per trovare ciò che rallenta l'applicazione. Inserisci l'API User Timing, che fornisce un meccanismo che puoi utilizzare per instrumentare la tua applicazione web in modo da identificare dove l'applicazione impiega il suo tempo. In questo articolo parleremo dell'API e forniremo esempi di come utilizzarla.

Non puoi ottimizzare quello che non puoi misurare

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

Tempo ad alta risoluzione e now()

Una parte fondamentale della misurazione accurata del tempo è la precisione. In passato, la misurazione del tempo si basava sui millisecondi, il che va bene, ma per creare un sito a 60 FPS senza jank, ogni frame deve essere disegnato in 16 ms. Pertanto, quando hai solo una precisione al millisecondo, manca la precisione necessaria per una buona analisi. Inserisci High Resolution Time, un nuovo tipo di misurazione del tempo ad alta risoluzione integrato nei browser moderni. High Resolution Time ci fornisce timestamp in virgola mobile che possono essere accurati fino alla risoluzione di un microsecondo, mille volte meglio di prima.

Per ottenere l'ora corrente nella tua applicazione web, chiama il metodo now(), che costituisce un'estensione dell'interfaccia Performance. Il seguente codice mostra come farlo:

var myTime = window.performance.now();

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

Il tipo DOMHighResTimeStamp

In passato, per misurare il tempo delle applicazioni web si utilizzava un elemento come Date.now(), che restituisce un DOMTimeStamp. DOMTimeStamp restituisce un numero intero di millisecondi come valore. Per fornire la maggiore precisione necessaria per l'ora ad alta risoluzione, è stato introdotto un nuovo tipo chiamato DOMHighResTimeStamp. Questo tipo è un valore in virgola mobile che restituisce anche l'ora in millisecondi. Tuttavia, poiché si tratta di un valore in virgola mobile, può rappresentare millisecondi frazionari e quindi può produrre un'accuratezza di un millesimo di millisecondo.

L'interfaccia Tempi utente

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

L'interfaccia User Timing fornisce funzioni che ci consentono di chiamare metodi in diversi punti della nostra applicazione, che possono fornire un percorso di navigazione in stile Hansel e Gretel per consentirci di monitorare dove viene impiegato il tempo.

In uso: mark()

Il metodo mark() è lo strumento principale del nostro toolkit di analisi dei tempi. mark() memorizza un timestamp per noi. La cosa più utile di mark() è che possiamo assegnare un nome al timestamp e l'API ricorderà il nome e il timestamp come un'unica unità.

Chiamando mark() in vari punti della tua applicazione, puoi calcolare il tempo necessario per raggiungere quel "punto" nell'applicazione web.

La specifica indica una serie di nomi suggeriti per i contrassegni che potrebbero essere interessanti e sono abbastanza autoesplicativi, come mark_fully_loaded, mark_fully_visible, mark_above_the_fold e così via.

Ad esempio, potremmo impostare un indicatore per quando l'applicazione è completamente caricata utilizzando il seguente codice:

window.performance.mark('mark_fully_loaded');

Impostando indicatori con nome in tutta l'applicazione web, possiamo raccogliere un'ampia gamma di dati di temporizzazione e analizzarli a nostro piacimento per capire cosa sta facendo l'applicazione e quando.

Calcolo delle misurazioni con measure()

Dopo aver impostato una serie di indicatori di tempo, ti consigliamo di scoprire il tempo trascorso tra l'uno e l'altro. A questo scopo, utilizza il metodo measure().

Il metodo measure() calcola il tempo trascorso tra i contrassegni e può anche misurare il tempo tra il tuo contrassegno e uno dei nomi evento più noti nell'interfaccia PerformanceTiming.

Ad esempio, puoi calcolare il tempo che intercorre tra il completamento del DOM e il caricamento completo 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 memorizzato indipendentemente dai contrassegni impostati, in modo da poterli recuperare in un secondo momento. Se memorizzi i tempi durante l'esecuzione dell'applicazione, quest'ultima rimane reattiva e puoi scaricare tutti i dati al termine di un'attività per analizzarli in un secondo momento.

Eliminazione dei voti con clearMarks()

A volte è utile poter eliminare un gruppo di indicatori che hai impostato. Ad esempio, potresti eseguire esecuzioni in batch sulla tua applicazione web e quindi voler iniziare da zero ogni esecuzione.

È abbastanza facile eliminare i contrassegni impostati chiamando clearMarks().

Pertanto, il codice di esempio riportato di seguito cancellerebbe tutti i contrassegni esistenti, in modo da poter configurare nuovamente un'esecuzione di misurazione, se vuoi.

window.performance.clearMarks();

Naturalmente, esistono alcuni scenari in cui potresti non voler cancellare tutti i tuoi segni. Quindi, se vuoi eliminare indicatori specifici, puoi semplicemente passare il nome dell'indicatore che vuoi rimuovere. Ad esempio, il codice seguente:

window.performance.clearMarks('mark_fully_loaded');

elimina il contrassegno impostato nel primo esempio, lasciando invariati gli altri contrassegni impostati.

Potresti anche voler eliminare le misurazioni effettuate. Per farlo, esiste un metodo corrispondente chiamato clearMeasures(). Funziona esattamente come clearMarks(), ma si applica a tutte le misurazioni effettuate. Ad esempio, il codice:

window.performance.clearMeasures('measure_load_from_dom');

rimuoverà la misura che abbiamo effettuato nell'esempio measure() riportato sopra. Se vuoi rimuovere tutte le misurazioni, la procedura è la stessa di clearMarks(): basta chiamare clearMeasures() senza argomenti.

Ottenere i dati di temporizzazione

È un'ottima cosa impostare indicatori e misurare intervalli, ma a un certo punto vorrai accedere ai dati di misurazione per eseguire un'analisi. Anche questo è molto semplice: basta usare l'interfaccia PerformanceTimeline.

Ad esempio, il metodo getEntriesByType() ci consente di ottenere tutti i tempi di marcatura o tutti i tempi di misurazione sotto forma di elenco, in modo da poterlo ripetere e analizzare i dati. La cosa interessante è che l'elenco viene restituito in ordine cronologico, quindi puoi vedere i contrassegni nell'ordine in cui sono stati raggiunti nella tua applicazione web.

Il codice seguente:

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

restituisce un elenco di tutti i contrassegni che sono stati raggiunti nella nostra applicazione web, mentre il codice:

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

restituisce un elenco di tutte le misurazioni effettuate.

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

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

restituisce un elenco con un elemento contenente il timestamp "mark_fully_loaded" nella proprietà startTime.

Misurazione del tempo di una richiesta XHR (esempio)

Ora che abbiamo un'idea abbastanza chiara dell'API User Timing, possiamo utilizzarla per analizzare il tempo impiegato da tutte le nostre XMLHttpRequests nella nostra applicazione web.

Innanzitutto, modificheremo tutte le nostre richieste send() per emettere una chiamata di funzione che imposta i contrassegni e, allo stesso tempo, modificheremo i nostri callback di esito positivo con una chiamata di funzione che imposta un altro contrassegno e poi genera una misura del tempo impiegato dalla richiesta.

In genere, il nostro XMLHttpRequest ha il seguente aspetto:

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

Per il nostro esempio, aggiungeremo un contatore globale per monitorare il numero di richieste e utilizzarlo anche per archiviare una misura per ogni richiesta effettuata. Il codice per farlo è il seguente:

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 di nome univoco per ogni XMLHttpRequest che inviamo. Supponiamo che le richieste vengano eseguite in sequenza: il codice per le richieste parallele dovrebbe essere un po' più complesso per gestire le richieste che restituiscono un ordine non corretto, ma lo lasceremo come esercizio per il lettore.

Una volta che l'applicazione web ha eseguito un gruppo di richieste, possiamo eseguirne il dump 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 offre molti strumenti utili da applicare a qualsiasi aspetto della tua applicazione web. Per restringere i punti critici della tua applicazione, puoi facilmente inserire chiamate API nella tua applicazione web e post-elaborare i dati di temporizzazione generati per creare un quadro chiaro di dove viene impiegato il tempo. Ma cosa succede se il tuo browser non supporta questa API? Nessun problema, puoi trovare un ottimo polyfill qui che emula l'API molto bene e funziona 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 migliorato la loro esperienza.