Node.js
Node.js
Node.js é uma plataforma que permite executar JavaScript fora do navegador. Originalmente o JavaScript foi criado para rodar apenas no browser, mas lá para 2008 o Google lançou o V8, um motor JavaScript OpenSource. Em 2009, Ryan Dahl utilizou o V8 para criar o Node.js, possibilitando assim que o JavaScript fosse executado no servidor.
Call Stack
A Call Stack é uma estrutura de dados que controla a ordem de execução das funções no JavaScript. Sempre que uma função é chamada, ela é adicionada na Call Stack. Quando termina sua execução, ela é removida. A Call Stack é um LIFO (Last In, First Out). O último que entra é o primeiro a sair.
Exemplo de código
function buga() {
return "buga";
}
function bugaBuga(cycles) {
return buga().repeat(cycles);
}
function printBuga(message) {
const result = message + " " + bugaBuga(3);
console.log(result);
}
printBuga("Morcego");
Como o JavaScript executa?
Hoisting
Antes de executar qualquer código, o JavaScript “iça” (hoisting) todas as declarações de funções para o topo do escopo.
buga()é declarada e armazenada na memóriabugaBuga()é declarada e armazenada na memóriaprintBuga()é declarada e armazenada na memória
Execução
Após o hoisting, o JavaScript executa o código de cima para baixo:
Ordem na Call Stack
printBuga("Morcego")entra na stack- Dentro de printBuga,
bugaBuga(3)é chamado e entra na stack - Dentro de bugaBuga,
buga()é chamado e entra na stack buga()retorna “buga” e sai da stackbugaBuga(3)completa (retorna “bugabugabuga”) e sai da stackprintBuga()executa o console.log e completa, então sai da stack
Resultando em
Morcego bugabugabuga
Single Thread
O Node.js é single-threaded, ou seja, possui apenas uma Call Stack. Isso significa que ele só pode executar uma tarefa por vez na thread principal.
Mas então, como o JavaScript consegue executar código assíncrono?
libuv
A libuv é uma biblioteca em C que implementa o Thread Pool e o Event Loop no Node.js. Ela é responsável por delegar operações assíncronas, permitindo que o JavaScript continue executando sem travar.
const fs = require('fs');
console.log("start")
fs.readFile('./test.txt', 'utf-8', function callback(_, data) {
console.log(data);
});
console.log("end")
Como o Node.js lida com operações bloqueantes?
O fs.readFile() é uma operação bloqueante ela leva tempo para ser executada. Se não tivéssemos a libuv, nosso programa ficaria travado esperando a leitura terminar antes de continuar a execução.
Mas então, como o Node.js resolve isso?
-
Thread Pool
fs.readFile()é removido da Call Stack- A libuv delega a tarefa para uma thread do Thread Pool
- A Call Stack fica livre para continuar executando outras tarefas
-
Processamento em background
- Enquanto a Call Stack continua executando o código, a thread do Thread Pool trabalha lendo o arquivo
- Quando a leitura termina, a função já processada com todo o texto vai para a Callback Queue
-
Callback Queue
- Ele fica na fila esperando o Event Loop
-
Event Loop
- O Event Loop monitora a Callback Queue e a Call Stack
- Quando detecta que a Call Stack fica vazia, ele pega o primeiro callback da fila, se tiver.
- O callback é movido para a Call Stack e finalmente executado