Node.js é uma plataforma open-source que permite a execução de código JavaScript no backend, ou seja, no servidor. Ele foi criado em 2009 por Ryan Dahl e se popularizou rapidamente entre os desenvolvedores backend devido a algumas vantagens importantes:
- Permite o desenvolvimento de aplicações web escaláveis de alta performance utilizando apenas JavaScript tanto no front-end quanto no back-end
- Possui uma arquitetura assíncrona e orientada a eventos que garante alta performance e escalabilidade
- Grande ecossistema de módulos e pacotes disponíveis através do NPM (Node Package Manager) que facilitam e agilizam o desenvolvimento
O Node.js é baseado no motor JavaScript V8 do Google Chrome e implementa o paradigma não bloqueante e dirigido por eventos que o torna leve e eficiente.
Em vez de threads, o Node.js usa uma única thread para lidar com todas as requisições. Quando alguma operação bloqueante precisa acontecer, ela é empurrada para segundo plano e um callback é chamado quando estiver pronta, permitindo que a thread continue lidando com outras requisições.
Essa arquitetura assíncrona permite lidar com milhares de conexões simultâneas em uma única máquina sem introduzir locks entre as requisições. É por isso que o Node.js é tão performático.
Como funciona o Node.js?
O Node.js funciona com base em alguns conceitos fundamentais:
Event Loop – É um loop infinito que fica verificando uma fila de callbacks e os executa assim que possível. Cada requisição ao servidor é tratada como um novo evento para ser processado pelo event loop.
Call Stack – É a pilha de execução, responsável por manter o controle sobre a ordem de execução do código JavaScript.
Callback Queue – Fila que armazena todos os callbacks que estão esperando para serem executados.
Event Emitter – Mecanismo que permite disparar e ouvir eventos, permitindo que diferentes partes da aplicação se comuniquem.
O fluxo básico de execução dentro do Node.js acontece da seguinte maneira:
- Uma requisição de rede chega ao servidor Node.
- O event loop pega a requisição e a coloca na fila de callbacks para ser processada assim que possível.
- Quando a thread principal estiver livre, o event loop pegará o próximo callback da fila e o executará.
- Se houver qualquer operação de E/S, como acesso ao banco de dados ou chamada de API, será utilizada uma thread separada para isso e um callback será chamado quando estiver pronto para continuar a execução.
- O processo se repete indefinidamente.
Essa arquitetura assíncrona e orientada a eventos é o grande segredo por trás da performance e escalabilidade do Node.js.
Principais recursos e funcionalidades
Alguns dos principais recursos e funcionalidades do Node.js são:
- NPM (Node Package Manager) – Gerenciador de pacotes e módulos open source mais grande do mundo. Permite instalar uma infinidade de pacotes com um simples comando.
- Async/Await – Permite trabalhar com promises de forma mais limpa e legível, utilizando async/await ao invés de callbacks aninhados.
- Streams – Permite trabalhar com fluxos de dados em chunks, sem precisar carregar dados grandes na memória. Útil para transferência de arquivos e streaming.
- Buffer – Permite trabalhar com streams de dados brutos, como manipulação de arquivos, sockets, entre outros.
- Single Thread e Event Loop – Toda a arquitetura assíncrona e orientada a eventos que garante a performance.
- Non-blocking I/O – Permite operações de entrada e saída sem bloquear a execução do código. Libera a thread principal enquanto aguarda I/O.
Alguns exemplos de uso são:
- APIs REST
- Sites e aplicações web real-time
- Ferramentas de linha de comando
- Automatização de tarefas
- Internet das Coisas
Por sua flexibilidade, leveza e performance, o Node.js conquistou rapidamente o seu espaço no backend e tem sido amplamente utilizado em aplicações web modernas.
Callbacks, Promises e Async/Await
Devido a sua arquitetura assíncrona, o Node.js tem algumas particularidades na hora de se trabalhar com código assíncrono.
Inicialmente, os callbacks eram utilizados. Um callback é uma função que é passada como parâmetro para outra função ser executada de forma assíncrona quando algum evento acontecer ou alguma tarefa terminar.
Porém, o uso excessivo de callbacks pode levar a um problema conhecido como “callback hell”, que é um código difícil de ler e manter devido aos callbacks aninhados.
Para resolver isso, o conceito de Promises (promessas) foi introduzido no JavaScript e também é amplamente utilizado no Node.js. Uma promise representa um valor que ainda não está disponível, mas que será no futuro.
Mais recentemente, o recurso async/await foi introduzido para simplificar ainda mais o uso de promises, permitindo escrever código assíncrono de forma quase síncrona.
Exemplo usando callbacks:
function doSomething(callback) { setTimeout(() => { // did something callback(); }, 1000);}doSomething(() => { // handled something});
Exemplo usando promises:
function doSomething() { return new Promise((resolve, reject) => { setTimeout(() => { // did something resolve(); }, 1000); });}doSomething().then(() => { // handled something });
Exemplo usando async/await:
async function doSomething() { return new Promise((resolve, reject) => { setTimeout(() => { // did something resolve(); }, 1000); });}async function main() { await doSomething(); // handled something}main();
O async/await permite escrever código assíncrono de forma muito mais limpa, como se fosse síncrono. Por isso é o método preferido hoje em dia.
Prós e Contras do Node.js
Prós:
- Orientado a eventos e assíncrono
- Single thread – mais leve e eficiente
- Altamente escalável
- Grande ecossistema de pacotes NPM
- Usa apenas JavaScript no frontend e no backend
- Comunidade muito ativa
Contras:
- Não é adequado para apps que demandam alta intensidade de processamento
- Não possui multithreading nativa
- Pode ser complexo para iniciantes
Conclusão
O Node.js conquistou rapidamente sua popularidade por trazer uma forma eficiente e escalável de construir aplicações web em JavaScript, tanto no front-end quanto no back-end.
Sua arquitetura assíncrona e single thread consegue lidar com muitas requisições simultâneas de forma muito performática e rápida, além de contar com um ecossistema gigantesco de pacotes NPM que agilizam o desenvolvimento.
Por esses motivos, o Node.js é hoje uma das principais tecnologias para se construir APIs REST, aplicações web em tempo real e ferramentas de linha de comando. Seu futuro parece ser bastante promissor a medida que mais funcionalidades são incorporadas.
Com isso, aprender Node.js é essencial para qualquer desenvolvedor back-end JavaScript que queira entregar aplicações mais escaláveis e flexíveis!