Antes de finalizar esse capítulo, quero propor um desafio. Já que aprendemos a utili-
zar os módulos http, url e fs (file system), que tal reorganizar a nossa aplicação para renderizar um determinado arquivo HTML baseado no path da url?
As regras do desafio são:
Crie 3 arquivos HTML: artigos.html, contato.html e erro.html;
Coloque qualquer conteúdo para cada página html;
19
2.5. Desafio: Implementar um roteador de url
Casa do Código
Ao digitar no browser o path: /artigos deve renderizar artigos.html;
A regra anterior também se aplica para o arquivo contato.html;
Ao digitar qualquer path diferente de /artigos e /contato deve renderizar erro.html;
A leitura dos arquivos html deve ser assíncrona;
A rota principal "/ deve renderizar artigos.html;
Algumas dicas importantes:
1) Utilize o retorno da função: url.parse() para capturar o pathname digitado
e renderizar o html correspondente. Se o pathname estiver vazio significa que
deve renderizar a página de artigos, e se estiver com um valor diferente do nome
dos arquivos html, renderize a página de erros.
2) Você também pode inserir conteúdo html na função: response.end(html),
economizando
linha
de
código
ao
não
utilizar
a
função:
response.write(html).
3) Utilize a função:
fs.exists(html) para verificar se existe o html com o
mesmo nome do pathname digitado.
O resultado desse desafio se encontra na página github deste livro:
https://github.com/caio-ribeiro-pereira/livro-nodejs/tree/master/desafio-1
20
Capítulo 3
Por que o assíncrono?
3.1
Desenvolvendo de forma assíncrona
É importante focar no uso das chamadas assíncronas quando trabalhamos com
Node.js, assim como entender quando elas são invocadas. O código abaixo exem-
plifica as diferenças entre uma função síncrona e assíncrona em relação a linha do
tempo que ela são executadas. Basicamente criaremos um loop de 5 iterações, a cada
iteração será criado um arquivo texto com o mesmo conteúdo Hello Node.js!.
Primeiro vamos começar com o código síncrono. Crie o arquivo text_sync.js
com o código abaixo:
var fs = require('fs');
for(var i = 1; i <= 5; i++) {
var file = "sync-txt"
+ i + ".txt";
var out = fs.writeFileSync(file, "Hello Node.js!");
console.log(out);
}
3.1. Desenvolvendo de forma assíncrona
Casa do Código
Agora vamos criar o arquivo text_async.js, com seu respectivo código, di-
ferente apenas na forma de chamar a função writeFileSync, que será a versão
assíncrona writeFile, recebendo uma função como argumento:
var fs = require('fs');
for(var i = 1; i <= 5; i++) {
var file = "async-txt" + i + ".txt";
fs.writeFile(file, "Hello Node.js!", function(err, out) {
console.log(out);
});
}
Vamos rodar? Execute os comandos:
node text_sync e depois node
text_async. Se for gerado 10 arquivos no mesmo diretório do código-fonte, en-
tão deu tudo certo. Mas a execução de ambos foi tão rápida que não foi perceptível
ver as diferenças entre o text_async e o text_sync. Para entender melhor as
diferenças, veja as timelines que foram geradas. O text_sync por ser um código
síncrono, invocou chamadas de I/O bloqueante, gerando o gráfico abaixo:
Figura 3.1: Timeline síncrona bloqueante.
Repare no tempo de execução, o text_sync demorou 1000 milissegundos, 200
milissegundos para cada arquivo criado.
Já em text_async foram criados os arquivos de forma totalmente assíncrona,
ou seja, as chamadas de I/O eram não-bloqueantes, sendo executadas totalmente em
paralelo:
22
Casa do Código
Capítulo 3. Por que o assíncrono?
Figura 3.2: Timeline assíncrona não-bloqueante.
Isto fez com que o tempo de execução levasse 200 milissegundos, afinal foram
invocados 5 vezes em paralelo a função fs.writeFile(), maximizando proces-
samento e minimizando o tempo de execução.
Threads vs Assincronismos
Por mais que as funções assíncronas possam executar em paralelo vá-
rias tarefas, elas jamais serão consideradas uma Thread (por exemplo Th-
reads do Java). A diferença é que Threads são manipuláveis pelo desen-
volvedor, ou seja, você pode pausar a execução de uma Thread ou fazê-la
esperar o término de uma outra. Chamadas assíncronas apenas invocam
suas funções numa ordem de que você não tem controle, e você só sabe
quando uma chamada terminou quando seu callback é executado.
Pode parecer vantajoso ter o controle sobre as Threads a favor de um
sistema que executa tarefas em paralelo, mas pouco domínio sobre eles
pode transformar seu sistema em um caos de travamentos dead-locks, afi-
nal Threads são executadas de forma bloqueante. Este é o grande diferen-
cial das chamadas assíncronas, elas executam em paralelo suas funções
sem travar processamento das outras e principalmente sem bloquear o
sistema principal.
É fundamental que o seu código Node.js invoque o mínimo possível de fun-
ções bloqueantes. Toda função síncrona impedirá, naquele instante, que o Node.js
continue executando os demais códigos até que aquela função seja finalizada. Por
23
3.2. Assincronismo versus Sincronismo