Como funciona o Event Loop em JavaScript?

Embora possa exigir uma compreensão profunda de linguagens como C++ e C para escrever código de produção em grande escala, o JavaScript pode ser escrito com apenas uma compreensão básica do que pode ser feito com a linguagem.

Conceitos, como passar callbacks para funções ou escrever código assíncrono, geralmente não são tão difíceis de implementar, o que faz com que a maioria dos desenvolvedores de JavaScript se importe menos com o que acontece nos bastidores. Eles simplesmente não se preocupam em entender as complexidades que os abstraíram profundamente pela linguagem.

Como desenvolvedor de JavaScript, torna-se cada vez mais importante entender o que realmente acontece nos bastidores e como a maioria dessas complexidades abstraídas de nós realmente funciona. Isso nos ajuda a tomar decisões mais informadas, o que pode, por sua vez, aumentar drasticamente o desempenho do nosso código.

Este artigo enfoca um dos conceitos ou termos muito importantes, mas raramente compreendidos, em JavaScript. O EVENTO LOOP!.

Escrever código assíncrono não pode ser evitado em JavaScript, mas por que um código rodando de forma assíncrona realmente significa? ou seja, o loop de eventos

Antes de entendermos como o loop de eventos funciona, primeiro precisamos entender o que é o próprio JavaScript e como ele funciona!

O que é JavaScript?

Antes de prosseguirmos, gostaria que voltássemos ao básico. O que realmente é JavaScript? Poderíamos definir JavaScript como;

JavaScript é uma linguagem simultânea, assíncrona, assíncrona, interpretada, de alto nível e sem bloqueio.

Espere, o que é isso? Uma definição livresca? 🤔

Vamos quebrar isso!

As palavras-chave aqui em relação a este artigo são single-threaded, non-blocking, concorrente e assíncrono.

Fio único

Um thread de execução é a menor sequência de instrução programada que pode ser gerenciada independentemente por um escalonador. Uma linguagem de programação é de thread único, o que significa que ela só pode executar uma tarefa ou operação por vez. Isso significa que ele executaria um processo inteiro do início ao fim sem que o thread fosse interrompido ou parado.

Ao contrário das linguagens multiencadeadas, nas quais vários processos podem ser executados em vários encadeamentos simultaneamente sem bloquear uns aos outros.

Como o JavaScript pode ser de thread único e sem bloqueio ao mesmo tempo?

Mas o que significa bloquear?

Sem bloqueio

Não existe uma definição de bloqueio; simplesmente significa coisas que estão rodando lentamente no encadeamento. Portanto, não bloquear significa coisas que não são lentas no encadeamento.

  6 melhores ferramentas de monitoramento de porta de switch

Mas espere, eu disse que o JavaScript é executado em um único thread? E eu também disse sem bloqueio, o que significa que a tarefa é executada rapidamente na pilha de chamadas? Mas como??? Que tal quando executamos temporizadores? Rotações?

Relaxar! Iríamos descobrir daqui a pouco 😉.

Simultâneo

Simultaneidade significa que o código está sendo executado simultaneamente por mais de um thread.

Ok, as coisas estão ficando realmente estranhas agora, como o JavaScript pode ser single-threaded e concorrente ao mesmo tempo? ou seja, executando seu código com mais de um thread?

Assíncrono

Programação assíncrona significa que o código é executado em um loop de eventos. Quando há uma operação de bloqueio, o evento é iniciado. O código de bloqueio continua em execução sem bloquear o thread de execução principal. Quando o código de bloqueio termina de executar, ele enfileira o resultado das operações de bloqueio e os empurra de volta para a pilha.

Mas JavaScript tem um único thread? O que então executa esse código de bloqueio enquanto permite que outros códigos no thread sejam executados?

Antes de prosseguirmos, vamos recapitular o que foi dito acima.

  • JavaScript é de thread único
  • JavaScript não bloqueia, ou seja, processos lentos não bloqueiam sua execução
  • JavaScript é concorrente, ou seja, executa seu código em mais de uma thread ao mesmo tempo
  • O JavaScript é assíncrono, ou seja, executa o código de bloqueio em outro lugar.

Mas o que foi dito acima não bate exatamente, como uma linguagem de encadeamento único pode ser sem bloqueio, simultânea e assíncrona?

Vamos um pouco mais fundo, vamos até os mecanismos de tempo de execução do JavaScript, V8, talvez ele tenha alguns encadeamentos ocultos que não conhecemos.

Motor V8

O mecanismo V8 é um mecanismo de tempo de execução de montagem da Web de código aberto e de alto desempenho para JavaScript escrito em C++ pelo Google. A maioria dos navegadores executa JavaScript usando o mecanismo V8, e até mesmo o popular ambiente de tempo de execução node js também o utiliza.

Em inglês simples, o V8 é um programa C++, que recebe o código JavaScript, o compila e o executa.

O V8 faz duas coisas principais;

  • Alocação de memória heap
  • Contexto de execução da pilha de chamadas

Infelizmente, nossa suspeita estava errada. O V8 tem apenas uma pilha de chamadas, pense na pilha de chamadas como o thread.

Um thread === uma pilha de chamadas === uma execução por vez.

Imagem – Hacker Noon

Como o V8 tem apenas uma pilha de chamadas, como o JavaScript é executado simultaneamente e de forma assíncrona sem bloquear o thread de execução principal?

  Tupla Python vs Lista: Semelhanças e Diferenças, Explicadas

Vamos tentar descobrir escrevendo um código assíncrono simples, mas comum, e analisá-lo juntos.

O JavaScript executa cada código linha por linha, um após o outro (single-threaded). Como esperado, a primeira linha é impressa no console aqui, mas por que a última linha é impressa antes do código de tempo limite? Por que o processo de execução não espera pelo código de tempo limite (bloqueio) antes de prosseguir para executar a última linha?

Algum outro thread parece ter nos ajudado a executar esse tempo limite, pois temos certeza de que um thread pode executar apenas uma única tarefa a qualquer momento.

Vamos dar uma espiada no Código Fonte V8 por um tempo.

Espere o que??!!! Não há funções de timer no V8, nenhum DOM? Sem eventos? Sem AJAX?…. Eeeeeesss!!!

Eventos, DOM, temporizadores, etc. não fazem parte da implementação principal do JavaScript, o JavaScript está estritamente em conformidade com as especificações Ecma Scripts e várias versões dele são frequentemente referidas de acordo com suas especificações Ecma Scripts (ES X).

Fluxo de Trabalho de Execução

Eventos, cronômetros e solicitações Ajax são fornecidos no lado do cliente pelos navegadores e geralmente são chamados de API da Web. São eles que permitem que o JavaScript de thread único seja não-bloqueante, concorrente e assíncrono! Mas como?

Existem três seções principais no fluxo de trabalho de execução de qualquer programa JavaScript: a pilha de chamadas, a API da Web e a fila de tarefas.

A Pilha de Chamadas

Uma pilha é uma estrutura de dados na qual o último elemento adicionado é sempre o primeiro a ser removido da pilha, você pode pensar nisso como uma pilha de uma placa na qual apenas a primeira placa que foi a última adicionada pode ser removida primeiro. Uma Pilha de Chamadas nada mais é do que uma estrutura de dados de pilha onde as tarefas ou o código estão sendo executados de acordo.

Vamos considerar o exemplo abaixo;

Fonte – https://youtu.be/8aGhZQkoFbQ

Quando você chama a função printSquare() , ela é colocada na pilha de chamadas, a função printSquare() chama a função square(). A função square() é colocada na pilha e também chama a função multiple(). A função de multiplicação é colocada na pilha. Como a função de multiplicação retorna e é a última coisa que foi enviada para a pilha, ela é resolvida primeiro e removida da pilha, seguida pela função square() e depois pela função printSquare().

A API Web

É aqui que o código que não é tratado pelo mecanismo V8 é executado para não “bloquear” o thread de execução principal. Quando a Call Stack encontra uma função da API da web, o processo é imediatamente entregue à API da Web, onde está sendo executado e liberando a Call Stack para realizar outras operações durante sua execução.

  Como definir um GIF como papel de parede ao vivo no seu iPhone

Vamos voltar ao nosso exemplo setTimeout acima;

Quando executamos o código, a primeira linha console.log é enviada para a pilha e obtemos nossa saída quase imediatamente. Ao atingir o tempo limite, os cronômetros são manipulados pelo navegador e não fazem parte da implementação principal do V8, são enviados em vez disso, para a API da Web, liberando a pilha para que ela possa executar outras operações.

Enquanto o tempo limite ainda está em execução, a pilha avança para a próxima linha de ação e executa o último console.log, o que explica por que obtemos a saída antes da saída do cronômetro. Assim que o cronômetro é concluído, algo acontece. O console.log em seguida, o cronômetro aparece magicamente na pilha de chamadas novamente!

Como?

O Ciclo de Eventos

Antes de discutirmos o loop de eventos, vamos primeiro examinar a função da fila de tarefas.

De volta ao nosso exemplo de tempo limite, uma vez que a API da Web termina de executar a tarefa, ela não apenas a envia de volta para a pilha de chamadas automaticamente. Ele vai para a Fila de Tarefas.

Uma fila é uma estrutura de dados que funciona de acordo com o princípio Primeiro a Entrar, Primeiro a Sair, de modo que, conforme as tarefas são colocadas na fila, elas saem na mesma ordem. As tarefas que foram executadas pelas APIs da Web, que estão sendo enviadas para a fila de tarefas, voltam para a pilha de chamadas para obter o resultado impresso.

Mas espere. O QUE DIABOS É O LOOP DO EVENTO???

Fonte – https://youtu.be/8aGhZQkoFbQ

O loop de eventos é um processo que espera que a Pilha de Chamadas seja limpa antes de enviar retornos de chamada da Fila de Tarefas para a Pilha de Chamadas. Depois que a pilha é limpa, o loop de eventos é acionado e verifica a Fila de tarefas em busca de retornos de chamada disponíveis. Se houver algum, ele o envia para a Pilha de Chamadas, espera que a Pilha de Chamadas seja limpa novamente e repete o mesmo processo.

Fonte – https://www.quora.com/How-does-an-event-loop-work/answer/Timothy-Maxwell

O diagrama acima demonstra o fluxo de trabalho básico entre o loop de eventos e a fila de tarefas.

Conclusão

Embora esta seja uma introdução muito básica, o conceito de programação assíncrona em JavaScript fornece informações suficientes para entender claramente o que está acontecendo nos bastidores e como o JavaScript pode ser executado simultaneamente e de forma assíncrona com apenas um único thread.

O JavaScript está sempre sob demanda e, se você estiver curioso para aprender, aconselho que verifique este curso udemy.