Diferenças entre async e defer
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:
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.
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">
O 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.
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.
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 script ser 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:
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 script tem, é minimizada por padrão.
Créditos
Asynchronous vs Deferred JavaScript , escrito originalmente por Ire Aderinokun
Adicionadas correções relacionadas à execução de script com atributo async (imagem) e parágrafo relacionado a execução sequencial do defer.