Javascript Assíncrono - Fluxo assíncrono e Event Loop

Javascript Assíncrono - Fluxo assíncrono e Event Loop

Nesse segundo artigo da série Javascript Assíncrono, vamos entender como funciona o fluxo assíncrono no javascript e o que é o Event Loop.

No artigo anterior aprendemos o que são callbacks e como funcionam, esse conhecimento é importante para termos o entendimento suficiente para compreender o conteúdo desse artigo e os demais da série. Aqui, iremos entender sobre o fluxo assíncrono do Javascript.

Fluxo síncrono

Antes disso vamos recapitular novamente o que é fluxo síncrono. Segue abaixo um exemplo.

1 console.log('Início do cálculo');
2 const resultado = 2 + 2;
3 console.log('Resultado é igual a:', resultado);
4 console.log('Fim do cálculo');

O resultado que aparecerá na tela (ou console) será:

Início do cálculo
Resultado é igual a: 4
Fim do cálculo

Como era de se esperar, o código foi executado linha por linha, da 1 até a linha 4, nessa sequência, de modo síncrono.

Por enquanto não há nenhum mistério por aqui, certo?

Javascript é single threaded, ou seja, opera em uma única thread, se você executar com código síncrono e por alguma eventualidade um comando bloquear a thread principal, o seu navegador (ou script no caso do node) irá travar até que a operação seja concluída.

Abaixo segue um bom exemplo adicionando o comando alert() no código anterior.

1 console.log('Início do cálculo');
2 const resultado = 2 + 2;
3 alert('Clique para continuar');
3 console.log('Resultado é igual a:', resultado);
4 console.log('Fim do cálculo');

Se executarmos esse código no navegador, a linha 1, 2 e 3 serão executadas. A terceira linha irá lançar um alerta na tela, isso irá bloquear o navegador, não permitindo nenhuma outra interação do usuário, ao menos que ele pressione o botão Ok do alerta. Após o clique no botão Ok as linhas 3 e 4 serão executadas.

Qual é o problema desse tipo de execução? imagina uma aplicação web como facebook, home banking, webmail ou youtube, se todo comando de pesquisa, ação de abrir um email ou execução de um vídeo bloqueasse a tela?

Essa maneira síncrona de execução é ruim porque não sabemos quanto tempo um vídeo será carregado, uma imagem será baixada ou uma resposta de um serviço será retornada, e pior, se ocorrerá um erro ou não.

Fluxo assíncrono

O que é assíncrono? de acordo com o dicionários na web:

Assíncrono: Que não ocorre ou não se efetiva ao mesmo tempo, que não acontece juntamente com outra coisa.

No YouTube, por exemplo, quando um vídeo está sendo carregado ou executado, essa operação é assíncrona, ela não bloqueia a tela do navegador, você pode realizar outra interação na tela do aplicativo web ao mesmo tempo que outras operações estão ocorrendo de forma assíncrona.

Mas como é possível execuções paralelas visto que o Javascript é single thread?

Agora vamos entrar em uma parte bem interessante, entender como o fluxo assíncrono funciona e assim responder a essa pergunta.

Call Stack

É o mecanismo do interpretador do Javascript, ele adiciona as funções para serem executa uma por vez, assim que forem executados ele remove da pilha.

Aqui nesse artigo vamos utilizar o loupe para simular como funciona o runtime do Javascript.

Abaixo um exemplo de sequência de execução de linhas contendo comandos console.log, você verá cada linha sendo executada e logo após removida do Call Stack.

Exemplo de código síncrono sendo executado loupe Código da imagem acima no loupe

Como falamos anteriormente, o Javascript é single threaded, podemos ver isso na Call Stack, código mal implementado pode bloquea-la facilmente. Abaixo podemos ver um comando while sendo executado infinitamente, o que acaba ocasionando o bloqueio da Call Stack

Exemplo de bloqueio da call stack ocasionado por um loop infinito Código da imagem acima no loupe

Agora um novo exemplo de funções sendo adicionadas na Call Stack.

Empilhamento de funções na call stack Código da imagem acima no loupe

Agora que entendemos com funciona a Call Stack vamos aprofundar o entendimento da parte assíncrona do Javascript, com explicações sobre Web APIs, Callback Queue e Event Loop.

Web APIs

Os navegadores fornecem Api's com funções assíncronas que podemos utilizar no nosso código, elas são executadas separadamente da Call Stack, evitando o seu bloqueio e realizando a execução em paralelo ao restando dos códigos.

Callback Queue

Quando utilizamos uma função assíncrona da Api do javascript, normalmente queremos reagir ao resultado dessa função, como por exemplo dados recebidos de uma chamada externa ou conteúdo de algum arquivo lido do disco local. Nesse momento entra a principal função dos callbacks, eles serão chamados quando função principal terminar sua execução e repassando para eles os seus resultados. Após isso os callbacks serão colocados na Callback queue, e aguardar até que a Call Stack esteja vazia para serem executados.

Event Loop

O Event Loop irá pegar o primeiro callback da Callback Queue e adicionar na Call Stack assim que a mesma estiver vazia. Se na Call Stack contiver algum código o Event Loop será bloqueado e não adicionará nenhum chamada até que a pilha esteja vazia.

Abaixo segue um exemplo utilizando setTimeout do Javascript, ele é uma função assíncrona que recebe um callback como parâmetro.

event_loop_04_callback_queue.gif Código da imagem acima no loupe

Agora vamos entender passo a passo do exemplo acima:

  1. Na linha 19 a função main() é chamada e colocada na Call Stack;
  2. Ao executar a função main() o javascript lê o comando console.log('Iniciando...') da linha 15, coloca na Call Stack e o executa, logo após o comando é removido da pilha;
  3. Na linha 16 a função funcao01() é chamada e colocada na Call Stack;
  4. Ao executar a função funcao01(), encontramos na linha 10 a chamada para a função buscaDados(), a mesma é colocada na Call Stack;
  5. Ao ler a função buscaDados(), o javascript encontra na linha 4 o comando setTimeout, como ele é uma função assíncrona o javascript o coloca na fila Web Apis para ser executado, aguardará 3 segundos e após isso seu callback simulaConsultaApi() é movido para a Callback Queue, para aguardar seu momento para entrar na Call Stack;
  6. Nesse momento temos na Call Stack, do topo da fila para baixo, as seguintes funções: buscaDados, funcao01 e main;
  7. Continuando no fluxo da Call Stack, a função buscaDados é removida da fila;
  8. O fluxo continua na linha 11, onde o comando console.log('funcao01'), é colocado na Call Stack, e após a sua execução ele é removido da pilha;
  9. A função funcao01() é removida da Call Stack;
  10. A função main() é removida da Call Stack;
  11. O fluxo segue na linha 20, o comando console.log('Fim!') é colocado na Call Stack e executado, logo após é removido;
  12. Como a fila da Call Stack está vazia, o Event Loop irá pegar o callback simulaConsultaApi() e coloca-la na Call Stack;
  13. Ao executar o callback simulaConsultaApi(), o comando console.log('Dados retornados') da linha 5 será posto na Call Stack, executado e logo após removido.
  14. E para finalizar, o callback simulaConsultaApi() será removido da Call Stack e o programa será finalizado.

Resumindo: O funcionamento em paralelo de funções no Javascript consiste em pegar funções assíncronas e coloca-las na Web Apis para serem executadas em paralelo a Call Stack. Ao finalizar sua execução ela pega o callback que lhe foi fornecido com o resultado e o coloca na Callback Queue, quando a Call Stack estiver vazia o Event Loop joga o callback na Call Stack para ser executado.

Conclusão

Essa explicações foram bem superficiais mas muito importantes para entendermos o que ocorre "por baixo do capô" do Javascript. Nos próximos artigos da série iremos entrar em mais detalhes e entender sobre Promises e Async/Await.

Espero que tenham gostado! Se puderem, deixem seus comentários e feedbacks :)

Referências