Introdução
O JavaScript é executado principalmente em um único thread no Node.js e no navegador (com algumas exceções, como threads de trabalho, que estão fora do escopo do artigo atual). Neste artigo, tentarei explicar o mecanismo de simultaneidade no Node.js que é o Event Loop.
Antes de começar a ler este artigo você deve estar familiarizado com a pilha e como ela funciona, escrevi no passado sobre essa ideia, então confira Stack & Heap — Não comece a codificar sem entendê-los — Moshe Binieli | Médio
Imagem de introdução
Exemplos
Acredito que aprender é melhor por meio de exemplos, por isso começarei com 4 exemplos de código simples. Analisarei os exemplos e depois mergulharei na arquitetura do Node.js.
Exemplo 1:
console.log(1);
console.log(2);
console.log(3);
// Saída:
//1
//2
//3
Este exemplo é bem fácil, na primeira etapa o console.log(1) vai para a pilha de chamadas, sendo executado e depois removido, na segunda etapa o console.log(2) vai para a pilha de chamadas, sendo executado e depois removido, e assim por diante para console.log(3).
Visualização da pilha de chamadas para o Exemplo 1
Exemplo 2:
console.log(1);
setTimeout(função foo(){
console.log(2);
}, 0);
console.log(3);
// Saída:
//1
//3
//2
Podemos ver neste exemplo que executamos setTimeout imediatamente, então esperaríamos que console.log(2) fosse antes de console.log(3), mas este não é o caso e vamos entender o mecanismo por trás disso.
Arquitetura básica de loop de eventos (falaremos mais sobre isso posteriormente)
Stack & Heap: Confira meu artigo sobre este (adicionei o link no início deste artigo)
Web Apis: Eles são integrados ao seu navegador da Web e são capazes de expor dados do navegador e do ambiente do computador circundante e fazer coisas úteis e complexas com eles. Eles não fazem parte da linguagem JavaScript em si, mas são construídos sobre a linguagem JavaScript principal, fornecendo superpoderes extras para usar em seu código JavaScript. Por exemplo, a API de geolocalização fornece algumas construções JavaScript simples para recuperar dados de localização para que você possa traçar sua localização em um mapa do Google. Em segundo plano, o navegador está, na verdade, usando algum código complexo de nível inferior (por exemplo, C ) para se comunicar com o hardware GPS do dispositivo (ou qualquer outro que esteja disponível para determinar os dados de posição), recuperar os dados de posição e devolvê-los ao ambiente do navegador para uso. no seu código. Mas, novamente, essa complexidade é abstraída de você pela API.
Event Loop e Callback Queue: As funções que finalizaram a execução do Web Apis estão sendo movidas para a Callback Queue, esta é uma estrutura de dados de fila regular, e o Event Loop é responsável por retirar da fila a próxima função da Callback Queue e enviar a função para a pilha de chamadas para executar a função.
Ordem de execução
Todas as funções que estão atualmente na pilha de chamadas são executadas e depois retiradas da pilha de chamadas.
Quando a pilha de chamadas está vazia, todas as tarefas na fila são colocadas na pilha de chamadas uma por uma e são executadas, e então são retiradas da pilha de chamadas.
Vamos entender o exemplo 2
O método
O método
Portanto, o parâmetro delay em setTimeout(function, delay) não representa o atraso preciso após o qual a função é executada. Significa o tempo mínimo de espera após o qual em algum momento a função será executada.
console.log(1);
setTimeout(função foo() {
console.log('foo');
}, 3500);
setTimeout(função boo() {
console.log('boo');
}, 1000);
console.log(2);
// Saída:
//1
//2
// 'vaia'
// 'foo'
Exemplo 4:
console.log(1);
setTimeout(função foo() {
console.log('foo');
}, 6500);
setTimeout(função boo() {
console.log('boo');
}, 2500);
setTimeout(função baz() {
console.log('baz');
}, 0);
para (valor const de ['A', 'B']) {
console.log(valor);
}
função dois() {
console.log(2);
}
dois();
// Saída:
//1
// 'A'
// 'B'
//2
// 'baz'
// 'vaia'
// 'foo'
O loop de eventos executa todos os retornos de chamada aguardando na fila de tarefas. Dentro da fila de tarefas, as tarefas são amplamente classificadas em duas categorias, nomeadamente microtarefas e macrotarefas.
Para ser mais preciso, existem na verdade dois tipos de filas.
As macrotarefas comuns são setTimeout, setInterval e setImmediate.
Microtarefas comuns são process.nextTick e Promise callback.
Ordem de execução
Todas as funções que estão atualmente na pilha de chamadas são executadas e, em seguida, retiradas da pilha de chamadas.
Quando a pilha de chamadas está vazia, todas as microtarefas enfileiradas são colocadas na pilha de chamadas uma por uma e são executadas, e então são retiradas da pilha de chamadas.
Quando a pilha de chamadas e a fila de microtarefas estão vazias, todas as macrotarefas enfileiradas são colocadas na pilha de chamadas, uma por uma, e executadas, e então são retiradas da pilha de chamadas.
Exemplo 5:
console.log(1);
setTimeout(função foo() {
console.log('foo');
}, 0);
Promessa.resolver()
.então(função boo() {
console.log('boo');
});
console.log(2);
// Saída:
//1
//2
// 'vaia'
// 'foo'
O método console.log(1) é chamado e colocado na pilha de chamadas e sendo executado.
SetTimeout está sendo executado, o console.log('foo') é movido para SetTimeout Web Api e 0 milissegundos depois ele é movido para a fila de tarefas de macro.
Promise.resolve() está sendo chamado, está sendo resolvido e então o método .then() é movido para a fila Micro-Task.
O método console.log(2) é chamado e colocado na pilha de chamadas e sendo executado.
O Event Loop vê que a pilha de chamadas está vazia, pega primeiro a tarefa da fila Micro-Task que é a tarefa Promise, coloca o console.log('boo') na pilha de chamadas e a executa.
O Event Loop vê que a pilha de chamadas está vazia, então vê que a Micro-Task está vazia, então pega a próxima tarefa da fila Macro-Task que é a tarefa SetTimeout, coloca o console.log('foo') na pilha de chamadas e a executa.
Visualização do processo para Exemplo 5
Estava pensando em escrever sobre o baixo nível de funcionamento do mecanismo Event Loop, poderia ser um post em si, então resolvi trazer uma introdução ao tema e anexar bons links que explicam o tema em profundidade.
Event Loop nível inferior explicado
O diagrama a seguir mostra uma visão geral simplificada da ordem de operações do loop de eventos. (Cada caixa será chamada de “fase” do ciclo de eventos. Confira a imagem de introdução para ter uma boa compreensão do ciclo.)
Visão geral simplificada da ordem de operações do loop de eventos
Cada fase possui uma fila FIFO de retornos de chamada para executar (digo isso com cuidado aqui porque pode haver outra estrutura de dados dependendo da implementação). Embora cada fase seja especial à sua maneira, geralmente, quando o loop de eventos entra em uma determinada fase, ele executará quaisquer operações específicas dessa fase e, em seguida, executará retornos de chamada na fila dessa fase até que a fila se esgote ou o número máximo de retornos de chamada executou. Quando a fila se esgotar ou o limite de retorno de chamada for atingido, o loop de eventos passará para a próxima fase e assim por diante.
Visão geral das fases
Retornos de chamada pendentes: executa retornos de chamada de E/S adiados para a próxima iteração do loop.
Ocioso, Preparar: usado apenas internamente.
Poll: recupera novos eventos de I/O; executa retornos de chamada relacionados a E/S (quase todos com exceção de retornos de chamada fechados, aqueles agendados por temporizadores e setImmediate()); o nó será bloqueado aqui quando apropriado.
Verifique: os retornos de chamada setImmediate() são invocados aqui.
Fechar retornos de chamada: alguns retornos de chamada próximos, por ex. soquete.on('fechar', ...).
Como as etapas anteriores se encaixam aqui?
Portanto, as etapas anteriores apenas com a “Fila de retorno de chamada” e depois com as “Filas de macro e micro” foram explicações abstratas sobre como funciona o Event Loop.
Há outra coisa IMPORTANTE a mencionar: o loop de eventos deve processar a fila de microtarefas inteiramente, após processar uma macrotarefa da fila de macrotarefas.
Etapa 1: O loop de eventos atualiza o tempo do loop para o horário atual da execução atual.
Etapa 2: a microfila é executada.
Passo 3: Uma tarefa da fase Timers é executada.
Passo 4: Verifica se há algo na Micro-Queue e executa toda a Micro-Queue se houver algo.
Passo 5: Retorna ao Passo 3 até que a fase Timers esteja vazia.
Etapa 6: Uma tarefa da fase de retornos de chamada pendentes é executada.
Passo 7: Verifica se há algo na Micro-Queue e executa toda a Micro-Queue se houver algo.
Etapa 8: Retorna à Etapa 6 até que a fase Retornos de chamada pendentes esteja vazia.
E então ocioso… Micro-fila… Pesquisar… Micro-fila… Verificar… Micro-fila… Fechar retornos de chamada e então recomeçar.
Então eu dei uma boa visão geral de como o Event Loop realmente funciona nos bastidores, há muitas partes faltando que não mencionei aqui porque a documentação real está fazendo um ótimo trabalho ao explicá-lo, fornecerei ótimos links para a documentação, incentivo você a investir de 10 a 20 minutos e entendê-la.
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3