Skip to content

Latest commit

 

History

History
180 lines (108 loc) · 7.08 KB

23.event_loop_do_node.md

File metadata and controls

180 lines (108 loc) · 7.08 KB

O Event Loop do Node.js

Índice

Introdução

O Event Loop é um dos aspectos mais importantes a serem entendidos sobre o Node.js

Por que isso é tão importante? Porque explica como o Node.js pode ser assíncrono e ter I/O sem bloqueio, e explica basicamente o "recurso matador" do Node.js, o que o tornou tão bem-sucedido.

O código JavaScript do Node.js é executado em um único encadeamento. Há apenas uma coisa acontecendo de cada vez.

Esta é uma limitação que é realmente muito útil, pois simplifica muito como você programa sem se preocupar com problemas de simultaneidade.

Você só precisa prestar atenção em como escreve seu código e evitar qualquer coisa que possa bloquear o encadeamento, como chamadas de rede síncronas ou loops infinitos.

Em geral, na maioria dos navegadores, há um event loop para cada guia do navegador, para tornar cada processo isolado e evitar uma página Web com loops infinitos ou processamento pesado para bloquear todo o navegador.

O ambiente gerencia vários event loops simultâneos, para lidar com chamadas de API, por exemplo. Os Web Workers também são executados em seu próprio event loop.

Você precisa se preocupar principalmente que seu código será executado em um único event loop com isso em mente para evitar bloqueá-lo.

Bloqueando o event loop

Qualquer código JavaScript que demore muito para retornar o controle ao event loop bloqueará a execução de qualquer código JavaScript na página, até bloqueará a thread da interface do usuário, e o usuário não poderá clicar, rolar a página e assim por diante.

Quase todas I/O primitivas em JavaScript não são bloqueantes. Solicitações de rede, operações do sistema de arquivos e assim por diante. O bloqueio é a exceção, e é por isso que o JavaScript é baseado tanto em retornos de chamada e, mais recentemente, em promises e async/await.

A pilha de chamadas

A pilha de chamadas é uma pilha FIFO (Last In, First Out).

O event loop verifica continuamente a pilha de chamadas para ver se há alguma função que precisa ser executada.

Ao fazer isso, ele adiciona qualquer chamada de função que encontrar à pilha de chamadas e executa cada uma em ordem.

Você conhece o rastreamento de pilha de erros com o qual pode estar familiarizado, no debugger ou no console do navegador? O navegador procura os nomes das funções na pilha de chamada para informar qual função origina a chamada atual:

Uma simples explicação de event loop

Vamos pegar um exemplo:

const bar = () => console.log('bar')

const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  bar()
  baz()
}

foo()

/* 
 foo
 bar
 baz
*/

Quando este código é executado, primeiro foo() é chamado. Dentro de foo(), primeiro chamamos bar(), depois chamamos baz().

Neste ponto, a pilha de chamadas se parece com isso:

O event loop em cada iteração verifica se há algo na pilha de chamadas e o executa:

até que a chamada na pilha fique vazia.

Execução da função de fila

O exemplo acima parece normal, não há nada de especial nele: JavaScript encontra coisas para executar, executa-as em ordem.

Vamos ver como adiar uma função até que a pilha esteja limpa.

O caso do uso de setTimeout(() => 0), é chamar uma função, mas executá-la assim que todas as outras funções código forem executadas.

Pegue este exemplo:

const bar = () => console.log('bar')

const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  setTimeout(bar, 0)
  baz()
}

foo()

Esse código imprime na seguinte ordem:

foo 
baz
bar

Quando este código é executado, primeiro foo() é chamado. Dentro de foo(), primeiro chamamos setTimeout(), passando bar como argumento, e o instruímos a executar imediatamente o mais rápido possível, passando 0 como o cronômetro. Então chamamos baz().

Neste ponto, a pilha de chamadas se parece com isso:

Aqui está a ordem de execução para todas as funções em nosso programa:

Por que isso está acontecendo?

A fila de mensagens

Quando setTimeout() é chamado, o navegador ou Node.js inicia o cronômetro. Uma vez que o temporizador expira, neste caso imediatamente quando colocamos 0 como o tempo limite, a função de retorno de chamada é colocada na Fila de Mensagens.

A **fila de mensagens **também é onde os eventos iniciados pelo usuário, como eventos de clique ou teclado, ou busca de respostas, são enfileirados antes que seu código tenha a oportunidade de reagir a eles. Ou também eventos DOM como onload.

O loop dá prioridade à pilha de chamadas e primeiro processa tudo o que encontra na pilha de chamadas e, uma vez que não há nada lá, ele pega coisas na fila de mensagens.

Não precisamos esperar que funções como setTimeout(), fetch() ou outras coisas façam seu próprio trabalho, porque elas são fornecidas pelo navegador e vivem suas próprias threads. Por exemplo, se você definir o tempo limite setTimeout() para 2 segundos, não precisará esperar 2 segundos - a espera acontece em outro lugar.

Fila de trabalho ES6

ECMAScript 2015 introduziu o conceito de Job Queue, que é usado por Promises (também introduzido no ES6/ES2015). É uma maneira de executar o resultado de uma função assíncrona o mais rápido possível, em vez de ser colocado no final da pilha de chamados.

Promessas que são resolvidas antes do término da função atual serão executadas logo após a função atual.

Semelhante a um passeio de montanha-russa em um parque de diversões: a fila de mensagens coloca você no final da fila, atrás de todas as outras pessoas, onde você terá que esperar sua vez, enquanto a fila de trabalhos é o ticket de livre acesso que permite que você pegue outro passeio logo após terminar o anterior.

Exemplo:

const bar = () => console.log('bar')

const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  setTimeout(bar, 0)
  new Promise((resolve, reject) =>
    resolve('deve ser depois do baz, e antes do bar')
  ).then(resolve => console.log(resolve))
  baz()
}

foo()

A saida vai ser exatamente:

foo
baz
deve ser depois do baz, e antes do bar
bar

Essa é uma grande diferença entre Promises (e Async/await, que é construído em promessas) e funções assíncronas simples por meio de setTimeout() ou outras APIs de plataforma.

Finalmente, aqui está a aparência da pilha de chamadas para o exemplo acima: