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:

  1. Uma requisição de rede chega ao servidor Node.
  2. O event loop pega a requisição e a coloca na fila de callbacks para ser processada assim que possível.
  3. Quando a thread principal estiver livre, o event loop pegará o próximo callback da fila e o executará.
  4. 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.
  5. 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!