Diferenças entre async e defer

Imagem relacionada

JavaScript é considerado um “bloqueador de recursos” enquanto é analisado. Isso significa que a renderização do HTML é bloqueada enquanto o código JavaScript é analisado. Quando o navegador encontra um elemento script, seja seu conteúdo interno (inline) ou externo (src=”arquivo.js”), todo o processo é colocado em espera até a finalização da requisição (em caso de arquivo externo) e execução desse conteúdo.

Esse comportamento pode ser problemático se nós carregarmos vários arquivos JavaScript em uma página, já que isso irá interferir no tempo de execução do primeiro evento de paint na página, mesmo que nosso documento HTML não dependa desses arquivos.

Felizmente, o elemento script tem dois atributos, async e defer, deixando a cargo do desenvolvedor um melhor controle de como e quando esses arquivos externos devem ser requisitados e executados.

Execução normal

Antes de olharmos no efeito desses dois atributos, vamos ver o que acontece na falta deles. Por padrão, como mencionado acima, arquivos JavaScript irão pausar a análise do documento HTML e ganhar prioridade na requisição/execução.

No documento abaixo, por exemplo, o elemento script está no meio da página:

<html>
<head>...</head>
<body>  
    ...
    <script src="script.js">
    ....
</body>
</html>

A análise feita pelo navegador fica mais ou menos assim:

normal-execution

A análise do HTML é pausada e o elemento script ganha precedência na requisição/execução. Aumentando terrivelmente o tempo necessário para a página receber seu primeiro evento de paint.

O atributo async

O atributo async é usado para indicar ao navegador que o script pode ser executado assincronamente. A análise do HTML não irá ser pausada quando encontrar esse elemento script – sua requisição ocorrerá paralelamente e sua execução pode acontecer a qualquer momento em que o script seja completamente carregado.

<script async src="script.js">  

Esse atributo só está disponível para script localizados em arquivos externos. Quando um script externo contém esse atributo, o arquivo pode ser requisitado enquanto o HTML está sendo analisado. Assim que terminado, a análise do HTML é pausada e a execução do script é realizada.

exec-async

O atributo defer

O atributo defer diz ao navegador para executar o script apenas quando a análise do HTML estiver finalizada.

<script defer src="script.js">

script será requisitado assincronamente, seu download será completado e, apenas quando a análise do documento HTML estiver finalizada, ele será executado. Mesmo se o download completo do script acontecer antes da análise completa do HTML, ele só será executado depois.

exec-defer

Caso você venha a ter múltiplos elementos script com o atributo defer.

<script defer src=“jquery.js">
<script defer src=“bootstrap.js">

Eles serão requisitados paralelamente e executados na sequência declarada.

exec-defer-multiple

Execução normal, async ou defer?

Depois de entendermos e analisarmos cada situação, fica a pergunta: quando devemos usar execução normal, async ou defer? Como sempre, depende da situação! E temos outros pontos a considerar também!

Onde o elemento script está localizado?

O elemento script com async e defer fazem mais diferença quando eles não estão localizados no final do documento HTML. A análise de documentos HTML acontece da esquerda para direita, de cima para baixo, começando com o primeiro elemento declarado html até quando ele é fechado. Se algum script externo está localizado logo antes do elemento /body, torna-se redundante o uso dos atributos async e defer. Como a análise do documento já está quase completa naquele momento, esses elementos script não tem muito o que bloquear.

Esse script não depende de outros?

Se os scripts externos que você está carregando não dependem de outros arquivos e/ou não tem nenhuma dependência própria, o atributo async geralmente é bem útil. Como você não precisa se importar muito a que momento ele será executado, carregá-lo assincronamente é a opção correta!

Esse script depende do DOM carregado?

Em vários casos, muitos script externos contêm funcionalidades que irão depender da interação com o DOM. Ou eles podem ter uma dependência em outro arquivo incluído na página. Nesses casos, o DOM precisa estar completamente analisado e carregado antes desse scriptser executado. Esse arquivo, tipicamente, será colocado no final da página para garantir que tudo tenha sido carregado antes de começar sua execução. Porém, em algumas situações, por quaisquer motivos, esse script em questão, terá quer ser colocado em outro lugar, então, usar o atributo defer é sua opção!

Esse script é uma dependência (pequena)?

Para finalizar, se o script for relativamente pequeno e/ou é necessário para outros arquivos, talvez seja mais útil defini-lo inline no documento. Mesmo que com o script inline uma pausa na análise do HTML ocorra, talvez ela não seja tão significante, não interferindo na experiência em geral. E, em casos em que outros arquivos dependem desse script, essa pequena pausa é necessária.

Suporte nos navegadores atuais

O suporte para async e defer é ótimo:

caniuse-async
caniuse-defer

Vale a pena notar que o comportamento desses atributos podem ter uma pequena diferença entre os interpretadores de JavaScript usados pelos navegadores. Por exemplo, no V8 (usado no Chromium), existe uma tentativa de analisar todos os elementos script, independentemente dos seus atributos, em uma thread separada e dedicada para execuções de scripts. Dessa maneira, essa “pausa” natural que o elemento scripttem, é minimizada por padrão.

Créditos

Resultado de imagem para async e defer

Sobre o atributo booleano defer e async vs otimização

O uso de async e defer para otimização é um bom assunto a tocarmos quando queremos páginas carregadas de forma mais rápida e também sem problemas de bloqueamento.

Dúvidas:

  • Usando async, se o navegador não der suporte, ele ignora o atributo e baixa/executa o script normalmente, ou ele simplesmente o ignora, não carregando-o?
  • Considerando que o objetivo do async e do defer, é realizar o carregamento dos scripts sem interromper/bloquear a renderização da página, não seria a mesma coisa que coloca-los(os scripts) antes do </body>? Ou por carregar assincronamente, ele é mais rápido já que não depende que a renderização seja concluída?
  • As bibliotecas para carregamento assíncrono, como o LABjs, ainda são úteis, tendo em vista estes atributos(async/defer)? Ou eles são de certa maneira mais seguros, garantindo o carregamento assíncrono independendo da versão do navegador?
  • Considerando que o defer permite que o script seja carregado de forma assíncrona, mas só permite sua execução após a renderização, ele pode ser melhor que o async, já que este executa após o carregamento, mesmo que ainda esteja ocorrendo a renderização?

Usando async, se o navegador não der suporte, ele ignora o atributo e baixa/executa o script normalmente, ou ele simplesmente o ignora, não carregando-o?

Ele ignora somente o atributo, o script é baixado normalmente como se não houvesse defer ou async, isto é, bloqueando o parse da página. Você pode verificar melhor quais navegadores suportam esses atributo por meio dos links: async e defer.

async é um atributo novo já o defer, se não me engano foi implementado pela Microsoft há muito tempo e então os demais navegadores passaram a dar suporte nas versões mais novas. Você pode usar a técnica de combinar os dois atributos para impedir o bloqueio da renderização e continuar baixando os scripts em segundo plano:

<script src='meuscript-js' async defer></script>

Se o navegador do usuário suportar ambos os atributos, async prevalecerá. Caso não suporte async, o atributo será ignorado e então defer será utilizado. Importante: A ordem de execução após o download é diferente e será explicada na próxima pergunta.

Considerando que o objetivo do async e do defer, é realizar o carregamento dos scripts sem interromper/bloquear a renderização da página, não seria a mesma coisa que coloca-los (os scripts) antes do </body>? Ou por carregar assincronamente, ele é mais rápido já que não depende que a renderização seja concluída?

Não deve levar em consideração somente o “carregar assincronamente”, vale ressaltar que cada atributo tem um comportamento e serve para um propósito.

Execução


Referência

async deve ser usado para scripts que podem ser executados independente do documento estar pronto ou não. O script será baixado sem interromper o parse da página e executado logo em seguida. No link de referência acima, um exemplo que bem simples para ilustrar são os scripts do Google Analytics. Não é preciso que o DOM esteja pronto para que eles sejam executados.

defer deve ser usado para scripts que devem ser executados somente quando o documento estiver pronto. Para exemplificar, um script que manipula o evento de click em uma tag <a> no DOM.

E só para deixar mais claro que não é a mesma coisa. Antes de </body>:

<script src='a.js'></script> <!-- será baixado e executado. -->
<script src='b.js'></script> <!-- será baixado depois de 'a.js' e executado. -->
<script src='c.js'></script> <!-- será baixado depois de 'b.js' e executado. -->

Já com async:

<!--
  Serão baixados todos ao mesmo tempo e executados após o download.
  Eles serão requisitados paralelamente e executados na sequência.
-->
<script async src='a.js'></script>
<script async src='b.js'></script>
<script async src='c.js'></script>

E com defer:

<!--
  Serão baixados todos ao mesmo tempo e executados somente quando a
  renderização do documento estiver concluída. Assim como o 'async',
  eles serão requisitados paralelamente e executados na sequência.
-->
<script defer src='a.js'></script>
<script defer src='b.js'></script>
<script defer src='c.js'></script>

As bibliotecas para carregamento assíncrono, como o LABjs, ainda são úteis, tendo em vista estes atributos(async/defer)? Ou eles são de certa maneira mais seguros, garantindo o carregamento assíncrono independendo da versão do navegador?

Aqui vai a questão de compatibilidade. Não conheço essas bibliotecas mas certamente elas tem como propósito (além do “carregamento assíncrono”) suprir a incompatibilidade dos navegadores. Se você não quer ter surpresas com async e defer, use uma biblioteca que trate isso.

Veja esse artigo no CSS-tricks

Considerando que o defer permite que o script seja carregado de forma assíncrona, mas só permite sua execução após a renderização, ele pode ser melhor que o async, já que este executa após o carregamento, mesmo que ainda esteja ocorrendo a renderização?

Não vejo um sendo melhor que o outro pois não servem para a mesma coisa, embora ambos fazem o download do script sem interromper a renderização mas o modo como esses scripts serão executados funciona de forma diferente.

Scripts que utilizam atributo async e defer fazem uma diferença maior quando eles não estão localizados tão no final do documento. Como a análise do HTML ocorre da esquerda para direita, de cima para baixo, começando pelo o primeiro elemento declarado <html>, até quando ele seja fechado. Se algum script está localizado logo antes do elemento </body>, torna-se redundante o uso dos atributos async e defer.

Como a análise do documento já está quase completa naquele momento, esses elementos script não tem muito o que bloquear.

Powered by Rock Convert

Renato Lucena

Developer PHP, Laravel. Goiania-GO https://www.linkedin.com/in/renato-de-oliveira-lucena-33777133/

Você pode gostar...