JavaScript sob o capô: loop de eventos

Dec 01 2022
Vamos dominar o JavaScript explorando seu funcionamento do zero Você já se deparou com 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 loop de eventos em relação ao contexto de execução, pilha de chamadas e fila de retorno de chamada realmente funcionam.

Vamos dominar o JavaScript explorando seu funcionamento do zero

Foto de Marc St no Unsplash

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 loopem relação a execution context, call stacke callback queuerealmente 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 -

  1. 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).
  2. 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-chavethis

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

Console do navegador

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 stackou 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 Contextou GECpelo compilador, bem como solicitado a colocá-lo no arquivo Call Stack.
  • Todo o seu código é executado um a um no Global Execution Contexte 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 Contextou FECé criado para executar o código da função e, em seguida, é adicionado ao topo do arquivo call stack.
  • O interpretador remove uma função de call stackcada 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 arquivo Call 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 f1e 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 FECserá 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 Contextserá 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 stackexecuta 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 stacknão tem qualquer temporizador. Mas como podemos fazer isso?

É aqui que entra o tempo de execução do JavaScript .

Ambiente de 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.

Fila de Rechamada

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 nosso call stacke GECexecuta nosso código linha por linha.
  • GECexecuta 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á definir 5000mscomo um tempo de atraso.
  • Quando você estiver passando a callBack()função pelo setTimeout(), o callBack()será registrado como um retorno de chamada pela API da Web da web.
  • E, em seguida, GECexecuta a 1ª linha e imprime 'End' no console.
  • Quando todos os códigos forem executados, GECserão removidos do nosso arquivo call stack.
  • Depois 5000 millisecond, a callBack()função que está registrada no web API, é movida para dentro do call Back Queue.
  • Event loopcoloca a callBack()função no call Stackquando é feito tudo funciona. E, finalmente, a callBack()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.

  1. A princípio GECserá criado dentro do call stacke depois o código será executado linha por linha no GEC. Armazena todas as definições de funções no Heap de Memória .
  2. Quando o main()é chamado, um Function Execution Context (FEC)é criado e, em seguida, fica dentro da pilha de chamadas. Depois disso, todo o código da main()função será executado linha por linha.
  3. Possui um log de console para imprimir a palavra main. Então o console.log('main')executa e sai da pilha.
  4. 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ão f2()entra na pilha. O log do console de f2()execuções. Ambos saem da pilha.
  5. E então o main()também sai da pilha.
  6. 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 pelo event loop. A execução é iniciada. O log do console é executado e f1()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 .