JavaScript sob o capô: loop de eventos
Vamos dominar o JavaScript explorando seu funcionamento do zero

Você já enfrentou erros indefinidos ou lutou para identificar o escopo de uma variável?
É muito demorado depurar qualquer erro sem saber como o código funciona.
Neste blog, demonstrarei como conceitos avançados como event loop
em relação a execution context
, call stack
e callback queue
realmente funcionam.
Um aviso - Os conceitos são incrivelmente intensivos em conhecimento e interconectados, então, por favor, nem pisque os olhos!
Mecanismo JavaScript
O mecanismo V8 do Google é uma ilustração bem conhecida de um mecanismo JavaScript. Por exemplo, Chrome e Node.js empregam o mecanismo V8. Basicamente, o motor V8 consiste em duas partes -
- Call Stack: Todo o código é executado dentro dela. Funciona como a estrutura de dados Stack, ou seja, segue o conceito LIFO (Last In First Out).
- Heap de memória: onde ocorre a alocação de memória. Ao contrário da estrutura de dados heap, é apenas uma memória.

O Contexto de Execução Global ou GEC é o Contexto de Execução padrão criado pelo mecanismo JavaScript sempre que um arquivo de script é recebido.
Todo código JavaScript que não está dentro de uma função é executado em GEC
. As etapas a seguir são executadas em GEC
-
- Construa um espaço de memória para armazenar todas as variáveis e funções em escala global
- Gere um objeto global.
- Gere a palavra-chave
this

Dependendo de onde seu código será executado irá determinar onde this
está localizado. Por exemplo, no Node.js ele aponta para um objeto global distinto, enquanto no navegador ele aponta para o window
objeto.

Pilha de chamadas (ou pilha de execução de função)
JavaScript é single-threaded
, como você já ouviu falar. Mas o que isso realmente significa?
Isso significa que um mecanismo JavaScript contém apenas um call stack
ou function execution stack
.
- Como sabemos, sempre que o compilador explora seu código pela primeira vez, o mecanismo JS é solicitado a criar um
Global Execution Context
ouGEC
pelo compilador, bem como solicitado a colocá-lo no arquivoCall Stack
. - Todo o seu código é executado um a um no
Global Execution Context
e aloca memória para definição de função ou declaração de variável e armazena lá. - Mas quando qualquer chamada de função é encontrada, um
Functional Execution Context
ouFEC
é criado para executar o código da função e, em seguida, é adicionado ao topo do arquivocall stack
. - O interpretador remove uma função de
call stack
cada vez que a função termina. Uma função termina — quando atinge o fim de seu escopo ou uma instrução de retorno. - Por fim, a execução de todo o seu código
GEC
é removida do arquivoCall Stack
.
Não se preocupe, vamos demonstrar com um exemplo.
function f1{
console.log('f1');
}
f1();
console.log('end');

Passo 2: — No nosso exemplo, será executada a 1ª linha que é f1
. Uma memória será alocada f1
e armazenada para sua definição.

Passo 3: — Na 2ª linha, uma função é chamada. Para esta chamada de função, um Function Execution Context
ou FEC
será criado e será armazenado no topo do arquivo Call Stack
.

Passo 4: — Agora, todo f1()
será executado linha por linha e após terminar a execução será removido do arquivo Call Stack
.

Passo 5: — Em seguida, a última linha console.log('end')
será executada e imprimirá 'end' no console. Finalmente, executando todo o seu código, Global Execution Context
será removido do arquivo Call Stack
.

Como JS gerencia tarefas assíncronas?
JavaScript, como todos sabemos, é uma linguagem síncrona de thread único (uma tarefa por vez) e o único thread que call stack
executa imediatamente tudo o que vem dentro dele.
Mas e se precisarmos executar algo após 5 segundos? Podemos vestir isso dentro do call stack
?
Não, não podemos. Porque call stack
não tem qualquer temporizador. Mas como podemos fazer isso?
É aqui que entra o tempo de execução do JavaScript .

Pode ser de partir o coração se eu disser agora setTimeout()
- não faz parte do JavaScript, embora todos console.log()
os eventos DOM sejam partes das APIs da Web que fornecem acesso ao JavaScript Engine para usar todas as suas propriedades no Global Execution Context (GEC) por meio de o objeto global window
. Essas APIs da Web são chamadas de assíncronas .
O que é fila de retorno de chamada?
Uma fila de tarefas chamada “fila de retorno de chamada” ou “fila de tarefas” é executada após a conclusão das tarefas atuais da pilha de chamadas. As tarefas registradas na API da Web são movidas da API da Web para a fila de retorno de chamada.
A fila de retorno de chamada funciona como uma estrutura de dados de fila, o que significa que as tarefas são tratadas na ordem FIFO (primeiro a entrar, primeiro a sair), em oposição a uma pilha de chamadas, o que significa que as tarefas são tratadas na ordem em que foram adicionadas à fila.

O que é Loop de Eventos?
Um loop de eventos JavaScript adiciona uma tarefa da fila de retorno de chamada à pilha de chamadas na ordem FIFO assim que a pilha de chamadas estiver vazia.
O loop de eventos é bloqueado se a pilha de chamadas estiver executando algum código e não adicionar chamadas adicionais da fila até que a pilha esteja vazia novamente. Isso ocorre porque o código JavaScript é executado de maneira completa.
Vamos entender os conceitos acima com um exemplo.
- A princípio,
Global Execution Context
é criado nosso código dentro de nossocall stack
eGEC
executa nosso código linha por linha. GEC
executa a 1ª linha e imprime 'Start' em nosso console.- Executando a 2ª linha,
setTimeout()
a API da web será chamada e, em seguida,setTimeout()
dará acesso ao recurso de timer. Então você poderá definir5000ms
como um tempo de atraso. - Quando você estiver passando a
callBack()
função pelosetTimeout()
, ocallBack()
será registrado como um retorno de chamada pela API da Web da web. - E, em seguida,
GEC
executa a 1ª linha e imprime 'End' no console. - Quando todos os códigos forem executados,
GEC
serão removidos do nosso arquivocall stack
. - Depois
5000 millisecond
, acallBack()
função que está registrada noweb API
, é movida para dentro docall Back Queue
. Event loop
coloca acallBack()
função nocall Stack
quando é feito tudo funciona. E, finalmente, acallBack()
função é executada e imprime 'Call Back' no console.








function f1() {
console.log('f1');
}
function f2() {
console.log('f2');
}
function main() {
console.log('main');
setTimeout(f1, 0);
f2();
}
main();
Se você está pensando que “f1” será impresso antes de “f2”, então você está errado. Será -
main
f2
f1
O mecanismo discutido do JavaScript é adequado para qualquer função de retorno de chamada ou solicitação de API.
Vamos observar passo a passo como o 2º exemplo funciona dentro do ambiente de execução.

- A princípio
GEC
será criado dentro docall stack
e depois o código será executado linha por linha noGEC
. Armazena todas as definições de funções no Heap de Memória . - Quando o
main()
é chamado, umFunction Execution Context (FEC)
é criado e, em seguida, fica dentro da pilha de chamadas. Depois disso, todo o código damain()
função será executado linha por linha. - Possui um log de console para imprimir a palavra main. Então o
console.log('main')
executa e sai da pilha. - A
setTimeout()
API do navegador ocorre. A função de retorno de chamada o coloca na fila de retorno de chamada. Mas na pilha, a execução ocorre normalmente, entãof2()
entra na pilha. O log do console def2()
execuções. Ambos saem da pilha. - E então o
main()
também sai da pilha. - O loop de eventos reconhece que a pilha de chamadas está vazia e que há uma função de retorno de chamada na fila. Portanto, a função de retorno de chamada
f1()
será colocada na pilha peloevent loop
. A execução é iniciada. O log do console é executado ef1()
também sai da pilha. E, finalmente, nada mais está na pilha e na fila para executar mais.
É minha prática e nota. Se você achou útil, mostre seu apoio clicando no ícone de palmas abaixo. Você pode me seguir no meio .