NodeJS
Node.js é uma plataforma construída sobre o motor JavaScript do Google Chrome para facilmente construir aplicações de rede rápidas e escaláveis. Node.js usa um modelo de I/O direcionada a evento não bloqueante que o torna leve e eficiente, ideal para aplicações em tempo real com troca intensa de dados através de dispositivos distribuídos.
Na JSConf 2009 Européia, um programador jovem chamado Ryan Dahl, apresentou um projeto em que estava trabalhando. Este projeto era uma plataforma que combinava a máquina virtual JavaScript V8 da Google e um laço de eventos. O projeto apontava para uma direção diferente das outras plataformas em JavaScript que rodam no servidor: todos I/O primitivos são orientado a evento. Aproveitando o poder e a simplicidade do Javascript, isso tornou tarefas difíceis de escrever aplicações assíncronas em tarefas fáceis. Desde quando foi aplaudido de pé no final do seu discurso, o projeto de Dahl tem recebido uma popularidade e uma aprovação sem precedentes.
Objetivo
O objetivo declarado do Node é “fornecer uma maneira fácil de criar programas de rede escaláveis”. Qual é o problema com os programas de servidor atuais? Vamos fazer as contas. Em linguagens como Java™ e PHP, cada conexão inicia um novo encadeamento que, potencialmente, é acompanhado de 2 MB de memória. Em um sistema que tenha 8 GB de RAM, isto define o número máximo teórico de conexões simultâneas em cerca de 4.000 usuários.
À medida que sua base de clientes cresce, você deseja que seu aplicativo da Web suporte mais usuários e, portanto, será necessário adicionar mais servidores. É claro, isso se soma a custos de negócios, especificamente custos de servidor, custos de tráfego e custos de mão de obra. Adicione a esses custos o problema técnico potencial de que um usuário poderá usar diferentes servidores para cada solicitação, de forma que quaisquer recursos compartilhados deverão ser compartilhados por todos os servidores. Por exemplo, no Java, variáveis estáticas e caches precisam ser compartilhados entre as JVMs em cada servidor. Este é o gargalo de toda a arquitetura de aplicativos da web, o número máximo de conexões simultâneas que um servidor pode tratar.
O Node soluciona o problema mudando a forma como uma conexão é feita no servidor. Em vez de iniciar um novo encadeamento do SO para cada conexão (e alocar a memória correspondente com ele), cada conexão cria um processo, que não requer que o bloco de memória o acompanhe. O Node alega que nunca ocorrerá um impasse de bloqueios, pois não são permitidos bloqueios e ele não bloqueia diretamente para realizar chamadas de E/S. O Node também alega que um servidor que o execute pode suportar dezenas de milhares de conexões simultâneas. De fato, o Node altera o panorama do servidor ao mudar o gargalo do sistema inteiro do número máximo de conexões para a capacidade de tráfego de um único sistema.
Características
Um Javascript “sem restrições”
Com NodeJS temos um “Javascript sem restrições”, já que tudo é executado no servidor e não temos que nos preocupar se nosso código será compatível ou não com diferentes clientes. Tudo que você escreva em Node JS e que funcione em seu servidor, estará seguro de que funcionará bem, seja qual for o sistema que se conecte, porque toda a execução de código do servidor fica isolada no servidor.
Um exemplo em Javascript:
for(var key in obj){ }
Um exemplo nas novas versões de Javascript:
Object.keys(obj).forEach()
Programação Assíncrona
Este é um conceito que algumas pessoas não conseguem entender de primeira e que agora toma especial importância, dado que NodeJS foi pensado desde o primeiro momento para potenciar os benefícios da programação assíncrona.
Imaginemos que um programa tem um fragmento de código que tarda cinco segundos em ser resolvido. Na maioria das linguagens de programação precedentes, durante todo esse tempo o fio de execução se encontra parado, esperando que passem esses cinco segundos, ou os que seja, antes de continuar com as instruções seguintes. Na programação assíncrona você é capaz de criar diferentes fios, com diferentes processos que levarão um tempo a serem executados, de modo que sejam feitos todos simultaneamente. Ademais, você poderá especificar código (callbacks) que se execute ao final de cada um desses extensos processos.
A filosofia detrás de Node.JS é fazer programas que não bloqueiem a linha de execução de código com respeito a entradas e saídas, de modo que os ciclos de processamento fiquem disponíveis quando se está esperando que se completem essas ações. Isto se implementa a partir de “callbacks” que são funções que se indicam com cada operação I/O e que se executam quando as entradas ou saídas foram completadas.
Nota: Estes callbacks são mais ou menos como os que podemos conhecer de outros sistemas Javascript como jQuery. Funções que são colocadas em execução quando terminam de serem executadas outras.
Por exemplo olhemos este código:
console.log("ola"); fs.readFile("x.txt", function(erro, arquivo){ console.log("arquivo"); }) console.log("ja!");
Realmente Javascript é síncrono e executa as linhas de código uma detrás de outra, mas pela forma de ser executado o código torna possível a programação assíncrona. A segunda instrução (que faz a leitura do arquivo) demora um pouco em ser executada e nela indicamos ademais uma função com um console.log (“arquivo”), essa é a função callback que só será executada quando termine a leitura do arquivo. A continuação, e antes que se termine a leitura do arquivo executa a instrução com o último console.log().
Como resultado, primeiro veremos a mensagem “ola” no console, depois a mensagem “ja!” e por último, quando o arquivo terminou sua leitura, veremos a mensagem “arquivo”.
Problema do código piramidal
O uso intensivo de callbacks na programação assíncrona produz o pouco desejável efeito de código piramidal, também conhecido habitualmente como “código Espaguete”. Ao se utilizarem os callbacks, se colocam umas funções dentro de outras e se vai entrando em níveis de profundidade que fazem um código menos simples de entender visualmente e de manter.
A solução é fazer um esforço adicional para estruturar nosso código, que basicamente se trata de modularizar o código de cada uma das funções, para escrevê-las à parte e ao indicar a função callback, em vez de escrever o código, escrevemos o nome da função que se definiu à parte. Você poderia, inclusive, definir as funções em arquivos à parte e requerê-las com require(“nome_arquivo”) no código de sua aplicação.
Ao conseguir níveis de indentação menos profundos, estamos ordenando o código, e com isso será mais simples de entender e também mais fácil de encontrar possíveis erros. Ademais, no fim das contas, conseguirá que seja mais escalável e possa estendê-lo no futuro ou mantê-lo por qualquer razão.
Alguns conselhos à hora de escrever código para que ele seja de maior qualidade:
- Escreva código modularizado (um arquivo com mais de 500 linhas de código pode estar mal proposto).
- Não abuse, não repita as mesmas coisas, melhor evitar.
- Use bibliotecas que ajudem o controle (como async que te ajuda a ordenar essa quantidade de callbacks).
- Use promessas e futuros.
- Use um estilo continuo (Exemplo: https://gist.github.com/4192304).
- Conheça a linguagem.
Programação orientada a eventos (POE)
Conhecemos a programação orientada a eventos porque a utilizamos em Javascript para escrever aplicações do lado do cliente. Estamos acostumados ao sistema, que em NodeJS é algo diferente, embora siga o mesmo conceito.
Em Javascript do lado do cliente temos objetos como “window” ou “document” mas em Node.JS não existem, pois estamos no lado do servidor.
Eventos que poderemos captar no servidor serão diferentes, como “uncaughtError”, que se produz quando se encontra um erro pelo qual um processo já não possa continuar. O evento “data” é quando chegam dados por um stream. O evento “request” sobre um servidor também se pode detectar e executar coisas quando se produza esse evento.
Voltamos a insistir, NodeJS seria um equivalente a PHP, JSP, ASP.NET e então tudo que sejam eventos de Node, serão coisas que ocorram no lado do servidor, diferentemente dos eventos de Javascript comum que são do lado do cliente e ocorrem no navegador.
Boas práticas de javascript
Padrões de código & melhores práticas em javascript
As principais vantagens de manter este padrão são:
- Consistência de código;
- Melhores práticas;
- Escalabilidade;
- Fácil manutenção;
- Uniformidade entre projetos.
A ideia é que o código mantenha unidade, seja ele escrito por uma ou várias pessoas, dentro de um projeto e entre projetos diferentes, diminuindo o tempo de aprendizado.
As regras a seguir propoem um padrão mínimo para desenvolvimento. O mais importante é manter a consistência de estilo de código. O que for escolhido como estilo para o seu projeto deverá ser seguido em todos os arquivos.
Padrões de escrita
Identação: utilizamos 2 espaços, mas o mais importante é seguir um estilo único. Nunca misture espaços e tabs em único arquivo. Para facilitar incluímos no projeto um arquivo de configuraçao de editores, o “.editorconfig”, saiba mais sobre ele em editorconfig.org
Existem plugins para vários editores, exemplo:
Plugin para Sublime Text
Plugin para Vim
Sintaxe: para separaçao dos blocos de código, utilizamos o estilo 1TBS, por exemplo:
if ( x < 0 ) { console.log( x ) } else { console.log( y ) }
Observe os alinhamentos, quebras de linhas, posição das chaves e espaços entre variáveis e operadores.
Revealing Module Pattern
Para melhorar a organização dos arquivos, funções, performance e diminuir a chance de erros e conflitos, adotamos o Revealing Module Pattern do Christian Heilmann.
As vantagens desse pattern são: organização, clareza, performance, expõe publicamente apenas as funções e variáveis que se deseja e namespace único evitando sobrescrever métodos facilmente.
Um exemplo:
// Define o objeto global do projeto, igual a um já existente ou cria um novo objeto. var projeto = projeto || {}; // Define o módulo no objeto global. projeto.moduloUm = (function() { 'use strict'; function iniciar() { interno(); // ... } function atualizar() { // ... } function interno() { // ... } return { iniciar:iniciar, atualizar: atualizar }; }());
O ideal é ter um módulo por arquivo, e que eles não se iniciem sozinhos.
Um módulo central que funcione como controlador de quais serão executados é uma boa prática.
Exemplo:
Módulo de gráficos no arquivo charts.js
var projeto = projeto || {}; project.Grafico = (function() { 'use strict'; function iniciar() { inserirGrafico(); } function inserirGrafico() { // ... } return { iniciar:iniciar }; }()); Módulo servidores no arquivo servers.js var projeto = projeto || {}; projeto.Servidores = (function() { 'use strict'; function iniciar() { buscaServidores(); } function buscaServidores() { insereListaServidores() // ... } return { iniciar:iniciar }; }());
Finalmente o arquivo que controla quando cada módulo será executado, da maneira que preferir:
Arquivo carregaModulos.js
var projeto = projeto || {}; projeto.carregaModulos = (function($) { 'use strict'; function inicio() { if ( $('.grafico').size() ) { projeto.graficos.inicio(); } if ( window.location.pathname === '/servidores' ) { projeto.servidores.inicio(); } } return { inicio:inicio }; }(jQuery)); projeto.carregaModulos.inicio();
Código em apenas um idioma
Para facilitar a manutençao e a legibilidade do código, procure usar apenas um idioma, em nomes de funções, variáveis e comentários.
O mais importante é seguir um padrão, mantendo ele do início ao fim do projeto. Exceções, claro, são libraries e plugins utilizados.
Seja qual for a língua utilizada é importante ser claro, utilizando nomes descritivos, evitando ser prolixo nos nomes, funções e comentários, como abordado no tópico “Seja compreensível” abaixo.
‘use strict’;
Ao utilizar “use strict”; no início de códigos ou funções, declara-se uma mudança na sintaxe do Javascript, que lança mais exceções mostrando problemas no código. Os erros mais comuns são uso de palavras reservadas do javascript (ECMAScript 5 e 6), uso de variáveis não declaradas e uso de funções descontinuadas.
Para utilizar, basta inserir a string ‘use strict’; antes do bloco de código a ser avaliado ou função. Exemplos:
"use strict"; idade = 17; // lança um erro de referência function soma(a, a, c){ // erro de sintaxe "use strict"; return a + b + c; // exibe o erro se a função é chamada } Leia mais em MDN - Strict mode
Nomenclatura de pastas, arquivos, módulos e plugins
Um projeto pode ter muitos arquivos javascript, então é importante que estejam bem organizados desde o início, separados e bem nomeados.
A estrutura pode variar muito de projeto para projeto, mas as recomendações mínimas são:
Nomes dos arquivos na mesma língua em que são escritos;
Nomes sem camelCase;
Pastas separadas por tipos (bibliotecas/frameworks/plugins);
Plugins identificados por versão.
Exemplo:
── javascripts
├── libs
│ ├── jquery.min.js
│ └── underscore.min.js
│ └── highcharts.min.js
├── plugins
│ ├── jquery.validate-1.2.js
│ ├── jquery.slider-3.1.js
│ └── jquery.validate.addicional-methods-0.8.js
├── modules
│ ├── charts
│ | ├── servers.js
│ | └── transfer.js
│ ├── servers.js
│ └── servers.forms.js
├── utils.js
└── main.js
Seja compreensível
Use nomes de variáveis e funções auto explicativos e simples. Crie algum padrão e mantenha em todo o projeto.
No Locastyle utilizamos variáveis e funções camelCase, pela facilidade de leitura, escrita e praticidade ao se trabalhar.
Exemplos:
Variáveis com nomes ruins:
// Curtos, posicionamento no código e abreviações var x1; var campo1; var latXY; // Longos demais var valorEixoXGraficoConsumo; var campoTextoPrimeiroNome; Funções com nomes ruins: // Nomes que descrevem o código, nao o objetivo da função function maiorDeDezoitoAnos(idade){ return idade >= 18; } // É melhor descrever o objetivo function possuiMaioridade(idade){ return idade >= 18; } É uma boa ideia criar um padrão para suas variáveis e funções, como por exemplo: // Variáveis com $ no início são elementos/objetos jQuery var $header = $('#header'); var $menuItens = $('#menu a'); // Maiúsculas para constantes var PASTA_IMAGENS = '/assets/images/'; var NOME_CLIENTE = 'Fulano de Tal'; // _ no início para variáveis e funções privadas var _contador = 0; Confira o padrão de nomenclatura do Douglas Crockford
Evite globais
No geral é uma péssima idéia, porque aumenta a chance de ter algo sobrescrito. Uma opção é utilizar closures e module pattern.
Seja consistente no estilo de código
É possível escrever seu código de muitas maneiras, mas procure manter o mesmo estilo em todo seu projeto. Mantendo um padrão nos nomes, identacões, patterns, etc.
Uma dica, utilize o jslint para validar seu código.
Escreva os comentários necessários
É comum ouvir “Um bom código não precisa de explicação”, mas na prática em projetos maiores, procure explicar a finalidade do seu código. Muitas pessoas, de diferentes níveis, podem ter que trabalhar no seu código e nem sempre elas tem experiência, tempo ou conhecimento do negócio para entender tudo. Facilite o desenvolvimento e manutenção comentando, mas não explicando o que ele faz, mas qual a regra de negócio.
Exemplo:
// Ruim: verifica se é maior de 18 // Bom: menores de idade sao redirecionados if( idade >= 18 ){ ... }
Lembrando que comentários devem existir apenas na versão de desenvolvimento, devendo ser removidos no arquivo minificado que é entregue em produção.
Modularize seu código
Evite escrever funções, trechos de código muito longos, ou aninhados. Procure separar regras e evite códigos repetidos.
Exemplo:
// Em vez de $('#botao1').on('click', function(){ $('#resultado').load('ajax/lista-pessoas.html', function() { $('#formulario').slideUp() }); }) $('#botao2').on('click', function(){ $('#resultado').load('ajax/lista-empresas.html', function() { $('#formulario').slideUp() }); }) //... // Faça function ocultaFormulario(){ $('#formulario').slideUp(); } $('#botao1').on('click', function(){ $('#resultado').load('ajax/lista-pessoas.html', function() { ocultaFormulario(); }); }) $('#botao2').on('click', function(){ $('#resultado').load('ajax/lista-empresas.html', function() { ocultaFormulario(); }); }) // Ou melhor function ocultaFormulario(){ $('#formulario').slideUp(); } function carregaDados( elemento, url){ $(elemento).on('click', function(){ $('#resultado').load(url, function() { ocultaFormulario(); }); }) } carregaDados('#botao1', 'ajax/lista-pessoas.html'); carregaDados('#botao2', 'ajax/lista-empresas.html');
Configurações e internacionalização
Quando estiver criando a aplicação, pense em cada valor, texto, variável, se é o caso de deixá-la separada, permitindo alterá-la depois.
Exemplo:
// Em vez de $.ajax({ type : "POST", url : "api/usuarios", data : { limite: 10 }, success: function( dados ){ $('#formulario').hide(); $('#mensagem').text( 'Busca efetuada com sucesso' ); $('#lista').html( dados ).fadeIn('fast'); }, error : function(){ $('#mensagem').text( 'Erro na busca' ); } }); // Faça var configuracoes: { mensagens = { successo : 'Busca efetuada com sucesso', erro : 'Erro na busca' }, api: { usuarios: "api/usuarios" }, animacao: { velocidade: "fast" }, lista: { porPagina: 10 } } //... $.ajax({ type : "POST", url : configuracoes.api.usuarios, data : { limite: configuracoes.lista.porPagina} , success: function( dados ){ $('#formulario').hide(); $('#mensagem').text( configuracoes.mensagens.successo ); $('#lista').html( dados ).fadeIn( configuracoes.animacao.velocity ); }, error : function(){ $('#mensagem').text( configuracoes.mensagens.erro ); } });
Utilização correta de Eventos
Ao escrever seu código e registrar comportamentos e eventos na interação do usuário é importante ter cuidado na escolha correta dos eventos.
Um caso comum de erro é vincular uma função no clique do botão enviar de um formulário, em vez de chamar a ação no submit do formulário:
// Errado // se o usuário submeter o form teclando ENTER a função não é executada $('#meuForm').delegate('#enviar', 'click', function(event){ if( $('#meuForm').isValid() ){ // ... ação } }); // correto // a ação é executada no clique ou no ENTER $('#meuForm').on('submit', function(event){ if( $('#meuForm').isValid() ){ // ... ação } });
Também é preciso saber o uso correto e diferenças entre os métodos, no caso do jQuery, sabendo os corretos para cada situação e os que estão em desuso.
// Os seguintes foram descontinuados .die() .load() .error() .unload() .live() .toggle() // os seguintes métodos podem ser evitados .mousedown() .blur() .rezise() .keyup() .keypress() .mousenter() .change() .hover() .ready() .focusin() .mouseleave() .click() .scroll() .submit() .mouseup() .mouseout() .dblclick() .keydown() .focusout() .mouseover() .focus() .select() // e devem ser substitídos por $('#elemento').on('blur', function(){ ... }) $('#elementoPai').delegate('#elemento', 'blur', function(){ ...}); // de prefrência o .delegate() que tem melhor performance // por fazer a seleção dentro de um escopo ( '#elementoPai' ) $('#escopo').delegate('#elemento', 'click', function(){ ...});
Evite muitos aninhamentos
Facilite o entendimento e manutenção dos seus códigos. Utilize as práticas anteriores e evite um código do tipo:
$('#botao').on('click', function(){ $.ajax({ type: "POST", url: 'usuarios', success: function(data){ $('resultado').fadeIn('fast', function(){ $('#formulario').animate({ heigth : 0, opacity: 0 }, 300, function(){ $('#mensagem').animate({ heigth : 200, opacity: 1 }, 300, function(){ // ... }) } ) }) }, error: function(){ $('#mensagem').text(mensageErro); } }); });
Otimize loops
Existem várias maneiras de fazer loops, uma melhores que outras. Hoje, é comum encontrar códigos usando loops do jQuery ($.each) onde loops nativos poderiam resolver. Uma técnica simples para melhorar ainda mais os loops for() nativos é fazer cache de variáveis de comparação, evitando executar o .length a cada iteração.
Exemplo:
// Use var cores = ['Azul', 'verde', 'rosa', 'vermelho']; for(var count=0, quantCores=cores.length; count < quantCores; count++){ // console.log( cores[i] ); } // em vez de var cores = ['Azul', 'verde', 'rosa', 'vermelho']; for(var count=0; i < cores.length; count++){ console.log( cores[i]) ; }
Para comparação, um teste de performance no jsperf analisando diversos tipos de loop. O loop otimizado, como acima, executou 159k vezes, contra 113k vezes do tradicional.
Minimize acessos ao DOM
Evite ao máximo acessar o DOM para buscar ou inserir informações. É lento e não se pode confiar no que está no DOM. Coisas simples podem minimizar o acesso, por exemplo, se alguma função precisa atualizar uma tabela no seu HTML, em vez de percorrer <td> por <td> alterando os valores, pode ser mais eficiente criar uma função que reescreva toda a tabela, e depois simplesmente troque a antiga pela nova. Outra simples é fazer cache de objetos jQuery, por exemplo:
// Em vez de $('#botaoEnviar').on( 'click', function(){ $(this).prop( 'disabled', true ); $('$formulario').addClass( 'enviando' ); $.post( 'ajax/contato', function( dados ) { $('#mensagem').html( dados ); $('$formulario').removeClass( 'enviando' ); $('#botaoEnviar').prop( 'disabled', false ); }); }); // Faça var $botaoEnviar = $('#botaoEnviar'); var $formulario = $('#formulario'); var $mensagem = $('#mensagem'); $botaoEnviar.on( 'click', function(){ $botaoEnviar.prop( 'disabled', true ); $formulario.addClass( 'enviando' ); $.post('ajax/contato', function( dados ) { $mensagem.html( dados ); $formulario.removeClass( 'enviando' ); $botaoEnviar.prop( 'disabled', false ); }); });
Adicione funcionalidades com javascript, não conteúdo
Se sua aplicação precisa criar e inserir muito HTML, códigos estão cheios de <div>, <td> e etc, analise e repense sua aplicação, veja se não é o caso de usar algum sistema de templates ( handlebars… ). São códigos dificeis de manter, que podem ter problemas de desempenho. Caso precise, use templates separados do JS, ou carregue HTML do back-end com AJAX.
Não reinvente a roda
Use bibliotecas e frameworks estáveis, que possuem estrutura conhecida.
Antes de inserir libraries e plugins, verifique a real necessidade, o suporte e compatibilidade.
Código de desenvolvimento não é código de produção
Compacte, remova comentários, concatene os javascripts antes de mandar para produção. Existem diversas ferramentas para isso, inclusive online. Tenha uma versão de desenvolvimento, normal, comentada e uma para o site em produção.
PHP vs. Node.js
PHP vs. Node.js: An epic battle for developer mind share
Here’s how the old guard and upstart darling of the server-side Web stack up against each other
Acesse o artigo completo em inglês
Comentários