Mergulhe profundamente no JavaScript interno
Funcionamento interno do mecanismo Javascript, loop de eventos, APIs da Web, filas de eventos e retornos de chamada.
Introdução
Você já imaginou o que teria acontecido se não houvesse Javascript? Na ausência do Javascript, você não conseguiria transmitir filmes no Netflix, fazer compras na Amazon ou assistir a vídeos no Youtube. Quase todos os outros sites usam Javascript e tornam a página interativa.
Antes de o Javascript ser inventado, a internet consistia apenas em páginas HTML simples . HTML e CSS dão estrutura e estilo a uma página da web. Mas o Javascript torna a página da Web dinâmica . Você pode inserir suas credenciais e fazer login em um site, realizar transações online ou jogar. Javascript faz tudo acontecer.
Uma das pesquisas realizadas no Stack-overflow mostrou que o Javascript tem o maior número de repositórios no GitHub. Todos os anos, novos frameworks e bibliotecas estão sendo desenvolvidos em Javascript. O idioma realmente transformou a internet e simplificou a vida das pessoas.
Fundo
Dois anos atrás, eu estava trabalhando em um projeto paralelo e usei Javascript pela primeira vez. Sendo um programador experiente desde a última década, tornei-me bem versado com a sintaxe e desenvolvi um bom comando em três meses.
Com o passar dos meses, a página da Web que desenvolvi não funcionava bem. Demorou muito para carregar e começou a impactar os clientes. Então, fiz um mergulho profundo para identificar a causa raiz.
Depois de passar noites sem dormir, finalmente perdi o juízo e estava prestes a desistir. No entanto, decidi fazer uma última tentativa. Por fim, pensei em obter uma compreensão completa de como o Javascript funciona internamente. Eu indiquei muitos recursos e finalmente consegui corrigir o bug no meu código em dois dias.
Engenheiros geralmente estão focados em construir coisas novas. As ferramentas e os frameworks que usamos escondem de nós a maior parte da complexidade. Em nossa jornada, tendemos a esquecer como as coisas realmente funcionam nos bastidores. É necessário conhecer o funcionamento interno de qualquer ferramenta que utilizamos. Ter uma boa compreensão dos internos nos ajuda a restringir o problema e encontrar uma solução pragmática.
Neste artigo, veremos o que acontece nos bastidores quando o Javascript é executado pelo navegador. Vamos dar uma olhada no mecanismo Javascript e entender seu funcionamento. Veremos os diferentes componentes envolvidos no tempo de execução do Javascript e obteremos clareza sobre o loop de eventos e a fila de eventos. Embora esses termos possam ser novos para você, o artigo tentará simplificar cada um deles. Vamos começar com nossa busca para dominar o funcionamento interno do Javascript.
Javascript
Javascript é uma linguagem interpretada de alto nível. Ao contrário de C ou C++, não precisamos compilar o código Javascript e depois executá-lo. O código Javascript é compilado primeiro em bytecode e depois traduzido em código de nível de máquina.
Linguagens avançadas como Java, C, C++, C# suportam multi-threading. Essas linguagens permitem que os programadores criem vários threads e executem as tarefas em diferentes threads. Assim, podemos gerar tarefas em várias CPUs e utilizar recursos de computação com eficiência.
Por outro lado, o Javascript é de thread único. Isso significa que há apenas um único thread de execução em um determinado momento. Caso você execute uma função javascript demorada, a página da Web congelará. Você pode ter visto o erro do site que não responde muitas vezes durante a navegação.
Vamos fazer uma demonstração rápida e ver o funcionamento em ação. Abri o Twitter e posso clicar nas guias individuais, como Trending, News, Sportsetc.
Agora, em meu developerconsole, estou escrevendo uma função que contém um loop infinito . Abaixo está a definição da função.
function infiniteLoop() {
for(i=0; i < 100000; ) {
}
}
infiniteLoop()
O GIF abaixo mostra que não consigo alterar a guia para Sportsou Entertaintment.
Como a função contém um loop infinito, ela continuará executando indefinidamente. Quando seleciono guias diferentes, o navegador deve manipular o clickevento e exibir o conteúdo da guia clicada. Não consigo selecionar nenhuma outra guia porque o Javascript já está executando a função infiniteLoop.
Como visto no exemplo acima, só podemos executar uma função por vez em Javascript. Agora, você pode questionar, como o navegador consegue carregar sites complexos em uma fração de segundos.
Vamos entender nas seções subsequentes como o Javascript faz essa mágica nos bastidores. Vamos agora entender o núcleo do Javascript, ou seja, Javascript Engine.
Mecanismo Javascript
Cada navegador fornece seu próprio Javascript Engine. Por exemplo:- O Chrome usa o mecanismo V8, enquanto o Firefox usa o SpiderMonkey Engine. O Engine é responsável por executar o código Javascript linha por linha. A seguir está uma lista de navegadores populares e seus mecanismos Javascript.
O mecanismo Javascript consiste em um Heap e um Stack . Vamos entender o funcionamento desses dois elementos.
pilha
O Heap é responsável por armazenar as definições de função, variáveis de objeto. Ele contém todos os objetos que estão sendo referenciados pelo código.
Os objetos são criados dinamicamente no heap. O funcionamento é semelhante ao java. Sempre que o objeto é referenciado por uma função, ele é obtido do heap.
function bar() {
var player = { "firstName": "Shubhman",
"lastName": "Gill"
}
var teams = ["India", "NewZealand"]
i =0
a = 10
console.log("In the function bar")
}
bar()
Objects on Heap
Pilha
A pilha é uma estrutura de dados LIFO (Last In First Out). Sempre que uma função é executada, as variáveis locais da função são colocadas na pilha. Depois que a função conclui a execução, ela é removida da pilha. Se uma função chama uma nova função durante a execução, a nova função também é enviada para a pilha. O mecanismo executa a função que está no topo da pilha.
As funções que estão na parte inferior da pilha retomam sua execução quando todas as funções acima retornam. Caso uma função gere um erro, um rastreamento de pilha é impresso no arquivo console. O rastreamento de pilha consiste na cadeia de funções que foram chamadas quando a exceção foi lançada. Vamos dar uma olhada em um exemplo.
Vou executar o código acima no meu navegador. E adicione um ponto de interrupção na função city.
Conforme visto acima, ao executar o script, ele parou no breakpoint da função city. No lado direito, na Call Stackseção, você pode ver a cityfunção na parte superior.
O Call Stackconsiste em todas as funções invocadas até agora. E a função mais recente é colocada no topo. Depois que a função conclui a execução, ela é removida da pilha.
Você pode ver na imagem abaixo, a função cityé removida e a execução continua para a função streetna linha número 9.
Como visto acima, o Javascript só pode executar uma função por vez e há apenas um thread de execução. Você não pode executar várias funções simultaneamente na pilha de execução.
Digamos que temos uma função que realiza uma HTTPchamada e obtém dados de outro site. O que aconteceria se o servidor estivesse lento e a função demorasse muito para ser executada? Não poderemos executar nenhuma outra função e a experiência do usuário será muito ruim.
Isso acontece no mundo real? Não, sites complexos como Facebook.com , Twitter.com , Amazon.com , etc executam toneladas de código Javascript sem afetar a experiência do usuário. Para entender como, vamos nos aprofundar no tempo de execução do Javascript.
tempo de execução do Javascript
Javascript nos fornece várias APIs para buscar dados do servidor, manipular o DOM ou executar funções usando um timer. Devido à natureza de thread único do Javascript, ele pode interromper a renderização de um site, levando a um desempenho ruim. No entanto, o tempo de execução do Javascript garante que isso não aconteça. Vamos entender isso com um exemplo.
Você pode adivinhar qual será a saída do código abaixo: -
Será getSubTitlesimpresso após a impressão getActors? E depois de ligar setTimeout, o código irá parar?
A seguir está a saída gerada pela execução do código acima: -
Como visto acima, o getSubTitlesfoi impresso primeiro e 4 segundos depois, getActorsfoi impresso. Embora, a função setTimeouttenha esperado por 4 segundos, mas ainda assim executou a função subseqüente getSubTitlesantes e o código não esperou. Vamos agora entender o que aconteceu nos bastidores.
APIs da Web
Você não encontrará a implementação de funções como setTimeout, ou fetchem qualquer lugar no código-fonte Javascript. Essas funções são fornecidas pelo navegador e não fazem parte do Javascript. A implementação seria diferente para cada navegador.
O navegador fornece funções para realizar chamadas de rede, manipular o DOM, processar áudio, vídeos, etc. Essas funções formam as APIs da Web. A implementação interna é complexa e consiste principalmente em código C++. As APIs nos fornecem uma interface que pode ser invocada a partir do código Javascript.
Quando você invoca uma função como setTimeout, o Javascript entende que esta é uma API da Web. Em seguida, ele delega a execução dessa função ao contêiner da API da Web. A função setTimeouté executada em um thread separado. Não é executado na thread de execução do Javascript. Esta é a razão pela qual o código escrito depois setTimeouté executado antes que a função de retorno de chamada seja passada para ele.
Da mesma forma, como as chamadas de rede são demoradas, as funções como fetchsão executadas em um thread separado. As APIs da Web do navegador ocultam a maior parte da complexidade de nós. Por exemplo: - a implementação interna de fetchconteria a formação de uma conexão TCP, enviando os dados e analisando-os. Além disso, também usaria os recursos de computação de forma eficiente, agendando a execução no thread apropriado.
O gif abaixo demonstra o que acontece quando você executa funções de API da Web como setTimeout.
Como visto acima, uma vez que a setTimeoutfunção é chamada, ela envia a função de retorno de chamada getActorspara as APIs da Web. No contêiner de APIs da Web, a função setTimeoutaguarda quatro segundos. Enquanto isso, a função subsequente getSubTitlesé chamada.
Agora, você pode perguntar como a função callback é getActorsexecutada após o término do timer. Veremos na próxima seção como os callbacks são executados após o retorno das APIs da Web.
Fila de eventos
Javascript depende fortemente de funções de retorno de chamada. As funções de retorno de chamada são executadas assim que ocorre um evento. Por exemplo:- Depois de receber uma resposta do servidor, precisamos imprimir a jsonrepresentação. Ou execute uma função após um atraso de 4 segundos.
O Javascript runtime consiste em um componente conhecido como Event Queue ou Callback Queue. É uma fila de mensagens e usa a ordem First In First Out (FIFO). As mensagens nada mais são do que funções de callback programadas para serem executadas.
Vamos dar um exemplo para entender isso. No código abaixo, estamos registrando uma função callback sempre que houver um evento click em um botão. Assim que o usuário clicar no botão, a função onClickserá chamada. Esta função chama a função timerapós um atraso de 2 segundos.
A animação acima mostra o que acontece nos bastidores. Temos duas APIs da Web aqui — a) $.one b) setTimeout. A primeira API aguarda a ocorrência de um evento de clique. Assim que receber um evento de clique, ele executará a função onClick. A segunda API setTimeOuté chamada posteriormente pela onClickfunção.
Toda vez que o usuário clica no botão, a função onClické enviada para a fila de eventos. Da mesma forma, após o término do cronômetro, a função timeré colocada na fila. Portanto, a fila de eventos é composta por todas as funções de retorno de chamada que devem ser executadas. Neste caso, onClicke a timerfunção.
Loop de eventos
As funções de retorno de chamada EventQueuealcançam a pilha de execução do Javascript por meio do Event Loop. O loop de eventos verifica continuamente se a pilha Javascript atual está vazia. Se descobrir que a pilha está vazia, ele pega a primeira mensagem da fila de eventos e a empurra para a pilha.
Tomando o exemplo da seção anterior, assim que o Event Loop descobrir que a pilha está vazia, ele pegará a próxima mensagem da fila de callback e a executará.
Abaixo está um pseudocódigo aproximado para o funcionamento do loop de eventos: -
Você pode pensar em um loop de eventos como um loop infinito que monitora continuamente a pilha de execução. Caso a pilha esteja vazia, ele irá pegar uma mensagem (função) da Fila de Eventos e executá-la.
O diagrama abaixo mostra a representação de alto nível do Javascript runtime.
Examinamos os diferentes componentes no tempo de execução do Javascript. Agora você terá uma ideia melhor de como o Javascript funciona nos bastidores. Vamos resumir o que aprendemos até agora.
Resumo
- O Javascript é de thread único e executa uma função por vez.
- Ele aproveita as APIs da Web para executar tarefas complexas em segundo plano.
- Depois que a execução da API da Web é concluída, ela envia as funções de retorno de chamada para a fila de eventos.
- O loop de eventos verifica continuamente se a pilha Javascript está vazia. Caso a pilha esteja vazia, ele pega a função callback da fila Event e a executa.
- Javascript interno e tempo de execução
- Como o Javascript funciona no navegador e no nó
- Um guia animado para o loop de eventos do Node.js
- O que diabos é loop de eventos, afinal?
- Javascript assíncrono e loop de eventos do zero





































![O que é uma lista vinculada, afinal? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)