Async ou Defer?

Carregando Javascript de maneira eficiente com defer e async

Compartilhe nas redes sociais

Você sabia que um script inserido em uma página HTML pode impactar diretamente na performance de carregamento? Pois é, dependendo da posição e como você o fizer, o tempo de carregamento da página será influenciado positiva ou negativamente.

Tradicionalmente inserimos um script assim:

<script src="script.js"></sript>

Sempre que o parser do HTML encontrar esta linha ele irá pausar a análise, uma requisição será feita solicitando o script, e então depois disso o seu código será executado.

Uma vez que este processo é concluído o parser continua seu trabalho analisando o resto  do HTML.

E bem aí está o problema

Como você pode imaginar, essa operação pode ter um impacto enorme no tempo de carregamento da página.

Se o script levar um pouco mais de tempo do que o esperado para ser carregado, por exemplo se a rede ou a internet do computador ou do celular estiver um pouco lenta, o visitante verá uma página em branco até que o script seja carregado e executado.

A posição importa!

Como eu já disse, quando o parser encontra a linha ele faz o fetch do script e após isso ele o executa. Somente depois de finalizar esse processo ele volta a analisar o HTML.

Isso é muito ruim, pois, começa a surgir um atraso no carregamento da página.

— Tá! E como solucionamos isso?

Isso é simples, uma solução muito usada é inserir o script ao fim do conteúdo da página, logo antes da tag </body>.

Ao fazer isso o script é carregado e executado depois que toda a página já foi analisada e carregada, o que é uma grande melhoria em relação à alternativa principal.

Caso você precise dar suporte a browsers realmente muito antigos, isso é o melhor que você poderá fazer. Caso contrário, você poderá usar uma de duas features mais recentes do HTML: async e defer.

Async e Defer

Ambos são atributos booleanos e são usados da seguinte maneira:

<script async src="script.js"></sript>
<script defer src="script.js"></sript>

Se você especificar os dois, somente o async será considerado pelos navegadores modernos, enquanto os browsers que não suportarem async mas sim defer, o usarão como fallback do async.

Para verificar o atual suporte dos browsers, você pode ver no can i use as informações para async e defer.

Vale ressaltar que o uso desses atributos só faz sentido caso você esteja inserindo o script dentro da tag head da página. Eles são totalmente inúteis se usados ao fim do body.

— Ok José, mas qual a diferença entre eles? Como eles funcionam?

A grande diferença está em como cada atributo faz com que o carregamento e execução do script aconteça.

Comparação de performance

Script no head e sem defer ou async

Veja abaixo como a página carrega o script sem nenhum dos atributos e com a chamada no head da página:

Imagem mostrando o desempenho do tempo de carregamento de uma página que uma tag script no head, sem os atributos async ou defer
Script no ‘head’, sem async ou defer

A análise do HTML é pausada até que o script seja recebido e executado. Ao finalizar a etapa do script a análise é retomada.

Script no body e sem defer ou async

Veja abaixo como a página carrega o script sem nenhum dos atributos e com a chamada no fim da tag body, logo antes de onde ela é fechada:

Imagem demonstrando como acontece a análise do html e o carregamento e execução de um script em uma página com a chamada do script no body e sem nenhum dos atributos
Script no ‘body’, sem async ou defer

A análise do HTML é feita sem nenhuma pausa, e quando é finalizada o script é baixado e executado. A análise é feita antes do download do script, de modo que a página seja exibida para o usuário antes do exemplo anterior.

Script no head com async

Veja como uma página carrega um script a partir do head da página e com o uso do atributo async:

Imagem mostrando como a página é carregada quando possúi um script sendo requisitado a partir do head e com o atributo async
Script no ‘head’, com async

O script é baixado de forma assíncrona e quando o download terminar, então, a análise do HTML é pausada para que o script seja executado, após a execução a análise é retomada.

Script no head com defer

Veja como uma página carrega um script a partir do head da página e com o uso do atributo defer:

Imagem mostrando como uma página é carregada quando possúi uma chamada para um script a partir do head com o atributo defer
Script no ‘head’, com defer

O script é baixado de forma assíncrona, porém será executado somente após a conclusão da análise do HTML.

A análise termina como quando colocamos o script no final da tag body, mas no geral a execução do script termina bem antes, porque seu download foi feito em paralelo com a análise do HTML.

— Uau! Isso quer dizer que o Defer é o campeão em termos de velocidade?

Sim!

Resumindo:

Async bloqueia a análise da página, Defer não!

Ótimo não é mesmo?

Sim! Mas vai devagar, pois, há um outro problema conhecido: O Bloqueio de renderização da página.

Tanto async quanto defer não garantem que a renderização da página não será bloqueada. Quem deve garantir isso é o seu código. Por exemplo, tendo certeza de que o código é executado depois do evento onLoad.

domInteractive

Os scripts marcados com defer são executados logo após o evento domInteractive, o que acontece depois que o HTML é carregado, analisado e o DOM é construído.

Neste ponto CSS e imagens ainda precisam ser analisados e carregados.

Feito isso, o navegador emitirá o evento domComplete e, em seguida, onLoad.

domInteractive é importante porque seu tempo é reconhecido como uma medida de percepção da velocidade de carregamento. Você pode saber mais no MDN.

Ei! Respeite a ordem de execução que eu defini.

Um outro pró do defer: scripts marcados como async são executados em ordem “aleatória”, na verdade, na medida em que vão ficando disponíveis. Já os marcados com defer são executados (após a conclusão da análise) na ordem em que são definidos na marcação.

— Ainda estou confuso José, me diga de uma vez qual o melhor!

A melhor coisa a se fazer para melhorar o tempo de carregamento da página é fazer a chamada do script no head e adicionar o atributo defer:

<script defer src="script.js"></sript>

Este é o cenário que acionará o evento domInteractive mais rápido.

Considerando todos os prós do defer, esta parece ser uma escolha melhor do que async em muitos cenários.

A não ser, é claro, que você esteja fazendo as coisas conscientemente, e propositalmente querendo que durante a análise da página seu Javascript já esteja sendo executado.

Bom, por hoje é só pessoal.

Dúvidas, críticas ou sugestões são sempre bem vindas.

Até breve!

Traduzido e adaptado de https://flaviocopes.com/javascript-async-defer/