Casa do Código
exemplo, se essa função fizer um I/O em disco, ele vai bloquear o sistema inteiro,
deixando o processador ocioso enquanto ele utiliza outros recursos de hardware,
como por exemplo leitura em disco, utilização da rede etc.
Sempre que puder, utilize funções assíncronas para aproveitar essa característica
principal do Node.js.
Talvez você ainda não esteja convencido. A próxima seção vai lhe mostrar como
e quando utilizar bibliotecas assíncronas não-bloqueantes, tudo isso através de teste
prático.
3.2
Assincronismo versus Sincronismo
Para exemplificar melhor, os códigos abaixo representam um benchmark compa-
rando o tempo de bloqueio de execução assíncrona vs síncrona. Para isso crie 3 arquivos: processamento.js, leitura_async.js e leitura_sync.js. Cri-
aremos isoladamente o código leitura_async.js que faz leitura assíncrona:
var fs = require('fs');
var leituraAsync = function(arquivo){
console.log("Fazendo leitura assíncrona");
var inicio = new Date().getTime();
fs.readFile(arquivo)
var fim = new Date().getTime();
console.log("Bloqueio assíncrono: "+(fim - inicio)+ "ms");
};
module.exports = leituraAsync;
Em seguida criaremos o código leitura_sync.js que faz leitura síncrona:
var fs = require('fs');
var leituraSync = function(arquivo){
console.log("Fazendo leitura síncrona");
var inicio = new Date().getTime();
fs.readFileSync(arquivo);
var fim = new Date().getTime();
console.log("Bloqueio síncrono: "+(fim - inicio)+ "ms");
};
module.exports = leituraSync;
Para finalizar carregamos os dois tipos de leituras dentro do código
processamento.js:
24
Casa do Código
Capítulo 3. Por que o assíncrono?
var http = require('http');
var fs = require('fs');
var leituraAsync = require('./leitura_async');
var leituraSync = require('./leitura_sync');
var arquivo = "./node.exe";
var stream = fs.createWriteStream(arquivo);
var download = "http://nodejs.org/dist/latest/node.exe";
http.get(download, function(res) {
console.log("Fazendo download do Node.js");
res.on('data', function(data){
stream.write(data);
});
res.on('end', function(){
stream.end();
console.log("Download finalizado!");
leituraAsync(arquivo);
leituraSync(arquivo);
});
});
Rode o comando node processamento.js para executar o benchmark. E
agora, ficou clara a diferença entre o modelo bloqueante e o não-bloqueante?
Parece que o método readFile executou muito rápido, mas não quer dizer que
o arquivo foi lido. Ele recebe um último parâmetro, que é um callback indicando
quando o arquivo foi lido, que não passamos na invocação que fizemos.
Ao usar o
fs.readFileSync(), bastaria fazer
var conteudo =
fs.readFileSync(). Mas qual é o problema dessa abordagem? Ela segura
todo o mecanismo do Node.JS!
Basicamente este código fez o download de um arquivo grande (código-fonte do
node.js) e, quando terminou, realizou um benchmark comparando o tempo de blo-
queio entre as funções de leitura síncrona (fs.readFileSync()) e assíncrona
(fs.readFile()) do Node.js.
Abaixo apresento o resultado do benchmark realizado em minha máquina:
Modelo: MacBook Air 2011
Processador: Core i5
Memória: 4GB RAM
25
3.3. Entendendo o Event-Loop
Casa do Código
Disco: 128GB SSD
Veja a pequena, porém significante diferença de tempo entre as duas funções de
leitura.
Figura 3.3: Benchmark de leitura Async vs Sync.
Se esse teste foi com um arquivo de mais ou menos 50 MB, imagine esse teste
em larga escala, lendo múltiplos arquivos de 1 GB ao mesmo tempo ou realizando
múltiplos uploads em seu servidor? Esse é um dos pontos fortes do Node.js!
3.3
Entendendo o Event-Loop
Realmente trabalhar de forma assíncrona tem ótimos benefícios em relação a proces-
samento I/O. Isso acontece devido ao fato, de que uma chamada de I/O é considerada
um tarefa muito custosa para um computador realizar. Tão custosa que chega a ser
perceptível para um usuário, por exemplo, quando ele tenta abrir um arquivo de 1
GB e o sistema operacional trava alguns segundos para abri-lo. Vendo o contexto de
um servidor, por mais potente que seja seu hardware, eles terão os mesmos bloqueios
perceptíveis pelo usuário, a diferença é que um servidor estará lidando com milhares
usuários requisitando I/O, com a grande probabilidade de ser ao mesmo tempo.
É por isso que o Node.js trabalha com assincronismo. Ele permite que você de-
senvolva um sistema totalmente orientado a eventos, tudo isso graças ao Event-loop.
Ele é um mecanismo interno, dependente das bibliotecas da linguagem C: libev
(http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod) e libeio (http://software.
schmorp.de/pkg/libeio.html) , responsáveis por prover o assíncrono I/O no Node.js.
A ilustração abaixo apresenta como funciona o Event-Loop:
26
Casa do Código
Capítulo 3. Por que o assíncrono?
Figura 3.4: Event-Loop do Node.js.
Basicamente ele é um loop infinito, que em cada iteração verifica se existem no-
vos eventos em sua fila de eventos. Tais eventos somente aparecem nesta fila
quando são emitidos durante suas interações na aplicação. O EventEmitter, é o módulo