Por que devo carregar Javascript assincronamente? E como faço isto?

Javascript, javascript… Até há pouco tempo diria que a relação dos desenvolvedores com a linguagem client-side era algo de amor ou ódio.

Muitos ainda pensam assim, mas a web 2.0 surgiu há alguns anos e ainda tem gente do próprio mercado que não percebeu: a web 2.0 traz novos paradigmas e novos desafios, dentre as quais temos que lidar com o imenso volume de tráfego de dados na rede (hoje até a comunidade mais carente está com acesso a internet. Foram publicadas pesquisas onde nos States o pessoal já consegue mais conteúdo pela banda larga do que TV por assinatura – a Netflix agradece).

Uma das formas de lidar com esta quantidade monstro de informação é dividir a responsabilidade do UX (user experience) entre o client-side e o server-side de maneira correta e consciente (num futuro eu entro nesta discussão sobre prós e contras de cada approach).  Mas quando falamos em client-side, não tem como fugir: Javascript – ou ame ou engula a seco, mas não ignore.

Falando especificamente de Javascript, ele é um dos principais responsáveis por perca de performance em uma aplicação. Ele é um cara que bloqueia a renderização do HTML pelo Browser . Ou seja, a página só é completamente carregada quando todos os seus scripts também são… Se tivermos scripts com problema é um “Deus nos acuda”.

Uma página geralmente é carregada de maneira síncrona… E até pouco tempo atrás nós simplesmente não nos importávamos com isto e deixávamos os scripts sendo carregados antes mesmo da página (dentro da TAG HEAD – o que era um padrão até então, visto que ninguém sofria muito com isto e ninguém tinha parado para pensar em uma alternativa). Veja um exemplo:

Plunker: http://plnkr.co/edit/nz4W7pcganTDhSENQOby

script.js
function Hello() {
alert(‘Veja que o conteudo so e exibido depois que voce der o OK no alert. Isto e a evidencia que a pagina HMTL é carregada de forma sequencial, sincrona e que o Javascript esta bloqueando a execucao da pagina’);
}

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src=”script.js”></script>
<script>
Hello();
</script>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
</body>
</html>

Neste caso, o script era carregado antes mesmo do conteúdo da página. Se você executar este código acima, verá que o alert fica preso até você dar o “OK” e somente depois a página (Hello World) é exibida. Ou seja, estamos bloqueando a execução do HTML.

Conforme esta prática se tornou um problema, uma nova maneira de carregar o script foi pensada e divulgada para toda a comunidade. Bastaria carrega-lo após a execução de todo o HTML, imagens e CSS. Assim a página seria exibida, mesmo que o script ainda não tivesse sido carregado. Para isto, é só carrega-lo antes da TAG </BODY >. Veja o exemplo:

script.js
function Hello() {
alert(‘Agora o conteudo e carregado antes do script. Isto por que o documento e carregado de forma sequencial e sincrona.’);
}

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
<script src=”script.js”></script>
<script>
Hello();
</script>
</body>
</html>

Esta simples prática resolveu uma boa parte dos problemas e continua sendo utilizada com bastante eficiência, pois não há mais o risco do usuário ficar olhando para as nuvens enquanto o script não é carregado… O carregamento continua sendo síncrono e sequencial, é verdade. Mas a UX aumenta bastante.

Porém esta ainda não é a solução ideal… O bacana mesmo é se o carregamento fosse assíncrono, pois isto faria uma página carregar bem mais rápida, otimizando assim a performance.

Uma solução clássica para carregar o script de forma assíncrona é construir o script de forma que possibilite o carregamento paralelo a página. Vejam:

Plunker: http://plnkr.co/edit/BzBH2wGjXyyS15xh5cLe

script.js
var content = document.getElementById(‘html_says’);
content.innerHTML = content.innerHTML + ” John Lennon!!!”;

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
<script>
(function () {
var po = document.createElement(‘script’);
po.type = ‘text/javascript’;
po.async = true;
po.src = ‘script.js’;
var s = document.getElementsByTagName(‘script’)[0];
s.parentNode.insertBefore(po, s);
})();
</script>
</body>
</html>

Isto garante que o carregamento do Javascript seja assíncrono, embora a sintaxe não seja muito agradável.  Uma solução para isto foi à inclusão do atributo async dentro do elemento script. Este atributo já foi implementado por todos os browsers – só o Internet Explorer que demorou bastante para implementar e só entrou na versão 10.

Para isto, basta carregar o script deste modo:
<script async src=”script.js”></script>

Simples, não? O Javascript será carregado e executado assim que o download for finalizado.

Mas e as versões mais antigas do IE? Como faremos? Um dos atributos especificados há muitos anos pela W3C para o elemento script é um chamado defer. Ele também faz o download assíncrono dos scripts, porém ele só irá executar quanto toda a página for carregada. Isto é muito interessante e, por vezes, muito desejado, pois resolve o problema das dependências de scripts – como na utilização do jQuery.

O problema é que os outros browsers foram na contramão do IE (que implementou o atributo desde versões mais antigas) e demoraram demais para implementar o defer… Então é comum você ter Safari, Chrome e Firefox sem suporte para o atributo. Para isto, a W3C especificou o seguinte: você poderá incluir o async e o defer dentro do mesmo elemento script. Um será fallback do outro, sendo que o async sempre sobrescreverá o defer quando o browser tiver o suporte para os dois.

Isto é maravilhoso para incluir scripts de AdSense e Analytics, que carregam sem depender de mais ninguém. Mas a dependência ainda é um problema. Vamos a um exemplo:

Plunker: http://plnkr.co/edit/1rO991HJurXhzylxf1J2

script.js
$(‘#html_says’).append(‘ John Lennon!!!’);

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
<script async defer src=”http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js”></script&gt;
<script async defer src=”script.js”></script>
</body>
</html>

Rodar isto aí num Chrome atualizado é apostar na sorte. Para resolver o problema alguns desenvolvedores simplesmente “empacotam” e compactam todos os scripts em um único arquivo js para o ambiente de produção (afinal em desenvolvimento ficaria inviável dar manutenção num negócio destes). Outros implementam callbacks para a página.

Porém, para nossa sorte, vendo bastante potencial no modelo assíncrono e já com a ideia de resolver as dependências, uma série de Script Loaders foram criados. E todos são bem interessantes e simples de serem utilizados.

Eu avaliei dois, que trago para vocês. O primeiro é o LABjs. A maior vantagem é sua simples utilização, muito parecido como o que já utilizamos hoje, além de ser extremamente leve. A principal desvantagem é que ele só resolve o problema do Javascript – se você quiser também carregar imagens e CSS assincronamente – o que também é interessante, embora exija certos cuidados, deverá procurar outra biblioteca. Vamos ao exemplo (não se esqueça de baixar a versão do LABjs em seu projeto):

Plunker: http://plnkr.co/edit/Cy6HEiLlINxpLsYU3vom

script.js
$(‘#html_says’).append(‘ John Lennon!!!’);

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
<script src=”LAB.min.js”></script>
<script>
$LAB
.script(“http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js&#8221;).wait()
.script(“script.js”);
</script>
</body>
</html>

Este método “wait()” é o que resolve a dependência, pois ele garante que este script (jQuery) será executado antes do “script.js”.

A biblioteca é muito estável e a mais de dois anos não precisa sofrer nenhuma alteração.

Já para quem trabalha com AMD (Asynchronous Module Definition) – ou seja – quase todos os novos especialistas em Front-end, principalmente os adeptos de SPA (single page application), a qual eu particularmente gosto bastante, a alternativa de Script Loader mais popular é o RequireJS, uma senhora ferramenta por sinal a qual certamente falaremos mais neste blog. Vamos ao exemplo (não se esqueça de baixar a versão do RequireJS no seu projeto):

Plunker: http://plnkr.co/edit/6u5hgyDvlBiqrALbl77K

script.js
$(‘#html_says’).append(‘ John Lennon!!!’);

main.js
require.config({
paths: {
“jquery”: “http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min&#8221;,
“myscript”: “script”
}
});

define([‘jquery’, ‘myscript’], function () { });

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
<script data-main=”main” src=”require.js”></script>
</body>
</html>

Por enquanto é isto pessoal! É muito importante trabalhar de forma assíncrona no HTML. Só com estas dicas de Javascript, é possível ganhar de 2x a 3x mais velocidade ao carregar suas aplicações – basta fazer o teste. Quando trabalhamos com AMD, é quase impossível pensarmos em trabalhar de maneira síncrona.

Há várias técnicas para carregar imagens e CSS do mesmo modo (sendo bem popular uma variante deste exemplo), só precisamos tomar alguns cuidados extras. Mas isto ficará para outro artigo.

Etiquetado , , , , ,

2 pensamentos sobre “Por que devo carregar Javascript assincronamente? E como faço isto?

  1. […] vamos baixar o LABjs para carregar os nossos scripts de modo assíncrono, conforme você aprendeu neste artigo. Coloque o arquivo LAB.min.js dentro da mesma pasta. Ficará […]

  2. Ranieri disse:

    Olá amigo, muito interessante seu artigo, parabéns, tenho uma duvida, utilizo o adsense em um projeto, porém preciso que o último anúncio carregue primeiro, não que ele apareça primeiro, mais sim, carregue antes de todos os outros anúncio, há alguma maneira de fazer isso? dar prioridade a ele?

    Desde já muito obrigado.

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: