API de velocidade do usuário

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

Alex Danilo

Os aplicativos da Web de alto desempenho são essenciais para uma ótima experiência do usuário. À medida que os aplicativos da Web se tornam cada vez mais complexos, entender o impacto no desempenho é essencial para criar uma experiência interessante. 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 necessariamente fornecem detalhes refinados com flexibilidade suficiente para descobrir o que está deixando seu aplicativo mais lento. Insira a API User Timing, que fornece um mecanismo para instrumentar seu aplicativo da Web e identificar onde ele está gastando tempo. Neste artigo, vamos falar sobre a API e dar exemplos de como usá-la.

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

A primeira etapa para acelerar um aplicativo da Web lento é descobrir onde o tempo está sendo gasto. Medir o impacto no tempo de á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 permite inserir chamadas de API em diferentes partes do JavaScript e extrair dados detalhados de velocidade do usuário que podem ser usados para 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 de 60 FPS sem instabilidade significa que cada frame precisa ser desenhado em 16 ms. Portanto, quando você tem apenas precisão de milissegundos, falta a precisão necessária para uma boa análise. Conheça o High Resolution Time, um novo tipo de medição de tempo integrado aos navegadores mais recentes. O High Resolution Time fornece carimbos de data/hora de ponto flutuante que podem ter precisão de microssegundos, mil vezes melhor do que antes.

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

var myTime = window.performance.now();

Há outra interface chamada PerformanceTiming que fornece vários tempos diferentes relacionados ao carregamento do seu aplicativo da Web. O método now() retorna o tempo decorrido desde o momento em que o tempo navigationStart em PerformanceTiming ocorreu.

O tipo DOMHighResTimeStamp

No passado, para medir o tempo de aplicativos da Web, 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 um milésimo de milissegundo.

A interface de velocidade do usuário

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

A interface de velocidade do usuário oferece funções que permitem chamar métodos em diferentes lugares do aplicativo, que podem fornecer um rastro de navegação no estilo João e Maria para que possamos acompanhar onde o tempo está sendo gasto.

Como usar o mark()

O método mark() é a principal ferramenta do nosso kit de análise de tempo. O mark() armazena um carimbo de data/hora para nós. O que é super útil sobre 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 lugares do aplicativo permite que você descubra quanto tempo levou para atingir essa "marca" no aplicativo da Web.

A especificação sugere vários nomes para marcas que podem ser interessantes e são 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 todo o aplicativo da Web, podemos coletar um monte de dados de tempo e analisá-los quando quisermos para descobrir o que o aplicativo está fazendo e quando.

Como calcular medições com measure()

Depois de definir várias marcações de tempo, você vai querer descobrir o tempo decorrido entre elas. Para isso, use o método measure().

O método measure() calcula o tempo decorrido entre as marcações e também pode medir o tempo entre sua marcação 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:

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

Quando você chama measure(), ele armazena o resultado independentemente das marcações que você definiu, para que possa recuperá-las mais tarde. Ao armazenar os tempos de execução do aplicativo, ele continua responsivo, e você pode despejar todos os dados depois que o aplicativo terminar algum trabalho para que eles sejam analisados mais tarde.

Como descartar marcações com clearMarks()

Às vezes, é útil poder se livrar de um monte de marcações que você configurou. Por exemplo, você pode fazer execuções em lote no seu aplicativo da Web e, por isso, quer começar do zero a cada execução.

É fácil se livrar de qualquer marcação que você tenha configurado chamando clearMarks().

Portanto, o exemplo de código abaixo vai apagar todas as marcações atuais para que você possa configurar uma execução de tempo novamente, se quiser.

window.performance.clearMarks();

Claro que há alguns cenários em que você não quer limpar todas as marcações. Se você quiser se livrar de marcas específicas, basta passar o nome da marca que quer remover. Por exemplo, o código abaixo:

window.performance.clearMarks('mark_fully_loaded');

elimina a marca que definimos no primeiro exemplo, mas deixa as outras marcas inalteradas.

Talvez você também queira se livrar de todas as medições que fez. Há um método correspondente para fazer isso, chamado clearMeasures(). Ele funciona exatamente como o clearMarks(), mas com as medições que você fez. Por exemplo, o código:

window.performance.clearMeasures('measure_load_from_dom');

vai remover a medição que fizemos no exemplo measure() acima. Se você quiser remover todas as medições, o processo é o mesmo que clearMarks(): basta chamar clearMeasures() sem argumentos.

Como extrair os dados de tempo

É ótimo definir marcas e medir intervalos, mas em algum momento você vai querer acessar esses dados de tempo para realizar uma análise. Isso também é muito simples. Basta usar a interface PerformanceTimeline.

Por exemplo, o método getEntriesByType() permite que você receba todos os tempos de marcação ou de medição como uma lista para que seja possível iterar sobre ela e digerir os dados. O bom é que a lista é retornada em ordem cronológica, para que você possa ver as marcas na ordem em que foram atingidas no aplicativo da Web.

O código abaixo:

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

retorna uma lista de todas as marcações que foram atingidas no nosso aplicativo da Web, enquanto o código:

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

retorna uma lista de todas as medições que fizemos.

Também é possível recuperar uma lista de entradas usando o nome específico que você deu 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 boa noção da API User Timing, podemos usá-la para analisar quanto tempo todas as XMLHttpRequests levam no nosso aplicativo da Web.

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

Normalmente, o XMLHttpRequest seria 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 rastrear o número de solicitações e também para armazenar uma medida para cada solicitação feita. O código para fazer isso é assim:

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 que enviamos. Estamos presumindo que as solicitações são executadas em sequência. O código para solicitações paralelas precisaria ser um pouco mais complexo para lidar com solicitações que retornam fora de ordem. Vamos deixar isso como um exercício para o leitor.

Depois que o aplicativo da Web tiver feito várias solicitações, podemos despejar todas elas 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. Para identificar os pontos de acesso no seu aplicativo, basta incluir chamadas de API no aplicativo da Web e processar os dados de tempo gerados para criar uma imagem clara de onde o tempo está sendo gasto. Mas e se o navegador não for compatível com essa API? Sem problemas, você pode encontrar um ótimo polyfill 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 mesmo. Você vai descobrir como acelerar a velocidade do usuário, e os usuários vão agradecer por uma experiência muito melhor.