API de velocidade do usuário

Noções básicas sobre seu app da Web

Alex Danilo

Aplicativos da Web de alto desempenho são essenciais para uma ótima experiência do usuário. À medida que os aplicativos da Web ficam cada vez mais complexos, entender o impacto no desempenho é vital para criar uma experiência envolvente. Nos últimos anos, várias APIs diferentes apareceram no navegador para ajudar a analisar o desempenho da rede, os tempos de carregamento etc., mas elas não fornecem necessariamente detalhes precisos com flexibilidade suficiente para encontrar o que está deixando seu aplicativo mais lento. Insira a API User Timing, que oferece um mecanismo para instrumentar seu aplicativo da Web a identificar onde ele está gastando tempo. Neste artigo, vamos falar sobre a API e conferir exemplos de como usá-la.

Não é possível otimizar o que não é possível medir

O primeiro passo para acelerar um aplicativo da Web lento é descobrir onde o tempo está sendo gasto. Medir o impacto do tempo em áreas do código JavaScript é a maneira ideal de identificar pontos de acesso, que é a primeira etapa para descobrir como melhorar o desempenho. Felizmente, a API User Timing oferece uma maneira de inserir chamadas de API em diferentes partes do JavaScript e extrair dados de tempo detalhados que podem ser usados para ajudar na otimização.

Tempo de alta resolução e now()

A precisão é uma parte fundamental da medição de tempo precisa. Antigamente, tínhamos um tempo baseado na medição de milissegundos, o que é bom, mas criar um site sem engasgos a 60 QPS significa que cada frame precisa ser renderizado em 16 ms. Portanto, quando você tem apenas a precisão de milissegundos, falta a precisão necessária para uma boa análise. Insira High Resolution Time, um novo tipo de temporização integrado aos navegadores modernos. O tempo de alta resolução fornece carimbos de data/hora de ponto flutuante que podem ser precisos até a resolução de microssegundos, mil vezes melhor do que antes.

Para saber a hora atual no seu aplicativo da Web, chame o método now(), que forma uma extensão da interface Performance. O código abaixo mostra como fazer isso:

var myTime = window.performance.now();

Há outra interface chamada PerformanceTiming, que fornece vários tempos diferentes relacionados à forma como o aplicativo da Web é carregado. O método now() retorna o tempo decorrido desde o momento em que o tempo navigationStart em PerformanceTiming ocorreu.

O tipo DOMHighResTimeStamp

Ao tentar cronometrar aplicativos da Web no passado, você usava algo como Date.now(), que retorna um DOMTimeStamp. DOMTimeStamp retorna um número inteiro de milissegundos como valor. Para fornecer a maior precisão necessária para o tempo de alta resolução, um novo tipo chamado DOMHighResTimeStamp foi introduzido. Esse tipo é um valor de ponto flutuante que também retorna o tempo em milissegundos. No entanto, como é um ponto flutuante, o valor pode representar milissegundos fracionários e, portanto, pode gerar uma precisão de milésimo de milissegundo.

A interface da User Timing

Agora que temos carimbos de data/hora de alta resolução, vamos usar a interface Tempo do usuário para extrair informações de tempo.

A interface de tempo do usuário oferece funções que permitem chamar métodos em diferentes lugares do aplicativo, que podem fornecer um rastro de migas no estilo Hansel e Gretel para acompanhar o tempo gasto.

Como usar o mark()

O método mark() é a principal ferramenta do nosso kit de ferramentas de análise de tempo. O mark() armazena um carimbo de data/hora para nós. O que é muito útil em mark() é que podemos nomear o carimbo de data/hora, e a API vai lembrar o nome e o carimbo de data/hora como uma única unidade.

Chamar mark() em vários locais do aplicativo permite que você descubra quanto tempo levou para atingir essa "marca" no aplicativo da Web.

A especificação chama a atenção para vários nomes sugeridos para marcas que podem ser interessantes e bastante autoexplicativos, como mark_fully_loaded, mark_fully_visible, mark_above_the_fold etc.

Por exemplo, podemos definir uma marca para quando o aplicativo estiver totalmente carregado usando o seguinte código:

window.performance.mark('mark_fully_loaded');

Ao definir marcas nomeadas em nosso aplicativo da Web, podemos reunir um monte de dados de tempo e analisá-los quando quisermos para descobrir o que o aplicativo está fazendo e quando.

Como calcular medidas com measure()

Depois de definir várias marcas de tempo, você vai querer saber o tempo decorrido entre elas. Para isso, use o método measure().

O método measure() calcula o tempo decorrido entre as marcas e também pode medir o tempo entre a marca e qualquer um dos nomes de eventos conhecidos na interface PerformanceTiming.

Por exemplo, é possível calcular o tempo desde a conclusão do DOM até o estado do aplicativo ser totalmente carregado usando um código como este:

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

Quando você chama measure(), ele armazena o resultado independente das marcas definidas para que você possa recuperá-las mais tarde. Ao armazenar os tempos à medida que o aplicativo é executado, ele continua respondendo, e você pode transferir todos os dados depois que o aplicativo terminar o trabalho para que possam ser analisados mais tarde.

Como descartar marcas com clearMarks()

Às vezes, é útil se livrar de várias marcas que você configurou. Por exemplo, você pode fazer execuções em lote no seu aplicativo da Web e, portanto, quer começar do zero em cada execução.

É fácil se livrar de todas as marcas que você configurou chamando clearMarks().

O exemplo de código abaixo vai apagar todas as marcas que você tem, para que você possa configurar uma execução de tempo novamente, se quiser.

window.performance.clearMarks();

É claro, há alguns cenários em que você talvez não queira limpar todas as suas marcas. Portanto, se você quiser se livrar de marcas específicas, basta transmitir o nome da marca que você quer remover. Por exemplo, o código abaixo:

window.performance.clearMarks('mark_fully_loaded');

se livra da marca que definimos no primeiro exemplo, deixando as outras marcas inalteradas.

Você também pode se livrar de todas as medidas que criou. Para isso, há um método correspondente chamado clearMeasures(). Ele funciona exatamente como o clearMarks(), mas trabalha em qualquer medição que você fez. Por exemplo, o código:

window.performance.clearMeasures('measure_load_from_dom');

vai remover a medida que fizemos no exemplo measure() acima. Se você quiser remover todas as medidas, o funcionamento será o mesmo que clearMarks(), ou seja, basta chamar clearMeasures() sem argumentos.

Como extrair os dados de tempo

É normal definir marcas e medir intervalos, mas, em algum momento, você quer receber dados nesse momento e fazer algumas análises. Isso também é muito simples: você só precisa usar a interface PerformanceTimeline.

Por exemplo, o método getEntriesByType() permite que você receba todos os tempos de marcação ou todos os tempos de medição em uma lista para iterar e processar os dados. O interessante é que a lista é retornada em ordem cronológica, para que você possa ver as marcas na ordem em que elas foram encontradas no aplicativo da Web.

O código abaixo:

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

retorna uma lista de todas as marcas que receberam hits em nosso aplicativo da web, enquanto o código:

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

retorna uma lista de todas as medidas que fizemos.

Também é possível receber uma lista de entradas usando o nome específico que você atribuiu a elas. Por exemplo, o código:

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

retornaria uma lista com um item que contém o carimbo de data/hora "mark_fully_loaded" na propriedade startTime.

Como cronometrar uma solicitação XHR (exemplo)

Agora que temos uma imagem decente da API User Timing, podemos usá-la para analisar quanto tempo todos os nossos XMLHttpRequests levam no nosso aplicativo da Web.

Primeiro, vamos modificar todas as solicitações send() para emitir uma chamada de função que configure as marcas e, ao mesmo tempo, mudar nossos callbacks de sucesso com uma chamada de função que defina outra marca e gere uma medida de quanto tempo a solicitação levou.

Normalmente, nosso XMLHttpRequest ficaria mais ou menos assim:

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

No nosso exemplo, vamos adicionar um contador global para acompanhar o número de solicitações e também para armazenar uma medida para cada solicitação feita. O código para fazer isso é parecido com este:

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

O código acima gera uma medida com um valor de nome exclusivo para cada XMLHttpRequest enviado. Estamos presumindo que as solicitações sejam executadas em sequência. O código para solicitações paralelas precisa ser um pouco mais complexo para processar solicitações que retornam fora de ordem. Vamos deixar isso como um exercício para o leitor.

Depois que o aplicativo da Web fizer várias solicitações, poderemos descarregá-las todas no console usando o código abaixo:

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

Conclusão

A API User Timing oferece muitas ferramentas excelentes para aplicar a qualquer aspecto do seu aplicativo da Web. É possível reduzir os pontos de acesso no seu aplicativo facilmente, distribuindo chamadas de API em todo o aplicativo da Web e processando os dados de tempo gerados para criar uma imagem clara de onde o tempo está sendo gasto. E se o navegador não for compatível com essa API? Sem problemas. Você pode encontrar um polyfill incrível aqui que emula a API muito bem e funciona bem com o webpagetest.org. Então, o que você está esperando? Teste a API User Timing nos seus aplicativos agora. Você vai descobrir como acelerar o processo e seus usuários vão agradecer por melhorar a experiência deles.