Performance

HTTP/2: o que você deve saber?

O protocolo HTTP é um protocolo importante, já que ele é a base da internet e das aplicações web na maneira que conhecemos hoje. E, como a maioria das tecnologias que são extensamente utilizadas, ele evoluiu. Sendo assim, temos uma nova versão do HTTP: o HTTP/2. Antes de tudo, caso não conheça o protocolo HTTP, sugerimos a leitura do artigo “O que é HTTP, Request, GET, POST, Response, 200, 404?”., onde os princípios básicos do protocolo HTTP são abordados. Mas, de forma bem resumida, o HTTP é um protocolo de transferência de arquivos e dados, que vão de um servidor até o cliente (navegador) que solicita estes arquivos e dados.

O protocolo HTTP/2

Já de início, devemos ressaltar que o HTTP/2 não muda a semântica do HTTP/1: tudo continua da mesma forma que já conhecemos, com seus verbos e status HTTP. O HTTP/2 foi desenvolvido para resolver alguns problemas que existiam na versão 1.1, principalmente com relação à performance. A maior parte das mudanças dizem respeito à forma que os recursos são transportados entre o cliente e o servidor, tornando esse processo muito mais performático, mesmo em cenários com múltiplas requisições simultâneas.

O HTTP/2 implementa um formato de entrega mais eficiente, acelerando o carregamento das páginas e elementos de uma aplicação web. Este é a principal vantagem que temos na utilização do HTTP/2. Mas ainda existem outras vantagens interessantes, que serão abordadas a seguir.

Multiplexação de mensagens

No HTTP/1, uma conexão é utilizada para baixar um único arquivo, tornando o processo de download de conteúdo algo sequencial. Ou seja: os recursos serão baixados uns atrás dos outros, até que essa “fila de downloads” acabe. Com isso, se um arquivo é muito grande e o servidor ficar lento no meio do processo de download, a página pode acabar passando por um congelamento. Mas o HTTP/2 implementou a multiplexação de mensagens. Com a multiplexação, o navegador não precisa mais ficar esperando receber um recurso para poder requisitar os recursos seguintes. Com o HTTP/2, o navegador pode solicitar e ter a resposta de vários recursos de forma paralela através de apenas uma conexão.

Server Push

Outra funcionalidade bem interessante é o Server Push. Com o HTTP/1.1 (versão anterior do protocolo), o navegador abria uma conexão para baixar cada item da página, se estivermos considerando a carga de uma página web: era uma conexão para baixar o CSS, outra conexão para baixar o JavaScript, e assim por diante, até que todo conteúdo vinculado à página fosse baixado completamente. Se estivermos falando de uma aplicação web que precisa de muitos recursos para ser renderizada, esse processo pode ser um pouco demorado… Já no HTTP/2, o servidor pode enviar recursos ao cache do navegador antes mesmo que o cliente o solicite! Com isso, é possível disponibilizar recursos (como arquivos JavaScript, CSS, imagens e outros recursos comumente utilizados por uma aplicação web) logo durante a interpretação do HTML.

Compressão de cabeçalho (HPACK)

Toda transferência contém um cabeçalho com diversas informações que descreve o recurso que está sendo transferido. Para reduzir a carga e melhorar o desempenho, o HTTP/2 utiliza um algoritmo chamado HPACK para compressão desse cabeçalho, gerando requisições mais leves e, consequentemente, fazendo com que os recursos sejam carregados mais rapidamente.

Priorização de requisições

O HTTP/1.1 tem dificuldade nesse quesito de priorizar requisições. Uma página web é composta por vários elementos. Estes elementos podem ter prioridades diferentes de carregamento no que diz respeito à carga de um recurso. Por exemplo: se estivermos falando de uma página web, não faz sentido que o CSS seja carregado antes de que o HTML esteja disponível.
O HTTP/2 soluciona este problema, já que é possível definir prioridades para os recursos a serem carregados por uma página web, por exemplo. Isso nos possibilitaria instruir o browser a baixar um arquivo CSS que poderia causar bloqueios no processo de renderização da página, caso fosse baixado tardiamente, com uma ordem de prioridade superior aos demais recursos.Esse tipo de priorização não existe no protocolo HTTP/1.1.

Obrigação na utilização de SSL (HTTPS)

Para adotar o HTTP/2, o uso de SSL se torna obrigatório. Isso em termos práticos quer dizer que todo conteúdo trafegado em conexões HTTP/2 estará criptografado com certificados SSL (HTTPS).

O nível atual de adoção do protocolo HTTP/2

Os principais navegadores (como o Chrome, Firefox, Safari e Edge) já oferecem suporte ao HTTP/2. É necessário que os clientes também suportem o HTTP/2 para que seja possível utilizá-lo. Alguns servidores famosos (como o Apache, Tomcat e Nginx) também já suportam o HTTP 2. Caso queira ver uma lista completa, você pode acessar por este link no GitHub.

Para que você perceba a diferença do HTTP/1.1 e do HTTP/2 na prática, você pode abrir o HTTP 2 – Technology Demo. Lá, existe um exemplo de um download de uma foto composta por 200 pequenas imagens através do protocolo HTTP/1.1 e através do protocolo HTTP/2. Nesse cenário, a melhor performance do protocolo HTTP/2 é explícita.

O que é Debounce e qual sua importância para a performance?

Olá, Web Developers!

Hoje vou falar sobre Debounce e a importância de saber quando usar em nossas aplicações.

Se você nunca usou essa técnica ao lidar com eventos como scroll, resize, mousemove, key*, etc, pode ser que enfrente problemas com seus dados e com a performance da sua aplicação.

Um exemplo básico

Imagine que temos um simples input do tipo texto. Nós vamos querer que, conforme o usuário vá escrevendo, alguns valores sejam exibidos como sugestão, assim como quando iniciamos uma busca no Google.

Então, quando o usuário para de escrever, temos que enviar o que ele escreveu para o nosso servidor para poder exibir as sugestões.

No exemplo abaixo, ao invés de fazer uma requisição, eu apenas incrementei um contador para vermos quantas requisições teríamos feito se essa fosse uma aplicação de verdade.

const myInput = document.querySelector('input'),
    mySpan = document.querySelector('span');
let counter = 0;

myInput.addEventListener('input', function(){
    mySpan.innerText = ++counter;
})

Vamos usar o evento “onInput” como exemplo. Se escrevermos “TreinaWeb” nesse input, veja que o contador vai acusar que o evento foi disparado 9 vezes.

Imagine só: apenas para enviar “TreinaWeb” para nosso servidor, teríamos feito 9 requisições se tivéssemos usado um simples evento “onInput” sem nenhum tratamento.

Pode parecer pouco, mas pense quando precisamos escutar outros eventos, como um scroll ou mousemove. Uma simples ação do usuário pode acabar disparando centenas ou até milhares de eventos.

Uma solução melhor é fazer uma verificação para saber se o usuário ainda está escrevendo. Se ele parou de digitar, então podemos enviar nosso texto para o servidor.

É aí que entra o Debounce. Ele permite a execução de uma função apenas se um determinado tempo se passou.

Um tratamento simples

Teste agora o exemplo abaixo com o tratamento para ver a diferença. Ao escrever com uma certa velocidade, o evento só será disparado quando você encerrar a digitação.

Mudamos pouca coisa no código:

const myInput = document.querySelector('input'),
    mySpan = document.querySelector('span');

let counter = 0,
    timer = null; // variavel para armazenar nosso timer

myInput.addEventListener('input', function(){
    // limpamos o timer
    clearTimeout(timer);
    // armazenamos o timer novamente
    timer = setTimeout(function(){
        mySpan.innerText = ++counter;
    }, 500);
})

Veja só o que fizemos:

Nossa função agora é executada dentro de um setTimeout. Estamos passando 500ms, ou seja, nosso código só será executado após meio segundo. Você pode mudar esse valor de acordo com sua necessidade.

Nós guardamos o resultado do setTimeout numa variável que chamamos de timer. Acontece que enquanto o usuário estiver digitando, menos de 500ms se passarão, então o nosso evento input será executado novamente. Veja que logo no começo dele nós limpamos o nosso timer com a função clearTimeout. Isso quer dizer que antes mesmo do nosso código ser executado, nós já cancelamos ele. E aí criamos outro setTimeout.

Caso o usuário pare de digitar, nosso evento onInput não será executado novamente, fazendo com que a linha onde executamos o clearTimeout() não seja executada, permitindo que o nosso código finalmente seja executado.

Bem simples e faz um grande benefício para nossas aplicações. Algumas bibliotecas, como o RxJS, nos fornecem já a opção de debounce para executarmos funções numa quantidade reduzida caso haja a possibilidade delas serem chamadas muitas vezes em um tempo muito curto.

Criando uma função “debounce” para reutilização

Tudo bem que só adicionamos menos de cinco linhas em nosso código para tratar uma função, mas imagine que a gente esteja em um sistema e a gente queira tratar várias funções.

Para isso, é melhor criar uma função que trate o debounce para nós.

const myInput = document.querySelector('input'),
    mySpan = document.querySelector('span');
let counter = 0;

function debounce(func, wait) {
    let timer = null;
    return function() {
        clearTimeout(timer);
        timer = setTimeout(func, wait);
    }
}

myInput.addEventListener('input', debounce(function(){
    mySpan.innerText = ++counter;
}, 500));

No código acima nós simplesmente criamos uma função debounce() que já faz o que conhecemos: tem uma variável “timer” e executa uma função que limpa o timer e executa nossa função após um determinado tempo.

Para essa função debounce() nós passamos a função que queremos executar e o tempo que ela deve esperar para ser executada.

Agora temos o mesmo comportamento e também temos a possibilidade de reutilizar essa função em outros lugares.

JUNTE-SE A MAIS DE 150.000 PROGRAMADORES