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.
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
Código da imagem acima no loupe
Agora um novo exemplo de funções sendo adicionadas 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.
Código da imagem acima no loupe
Agora vamos entender passo a passo do exemplo acima:
- Na linha
19
a funçãomain()
é chamada e colocada na Call Stack; - Ao executar a função
main()
o javascript lê o comandoconsole.log('Iniciando...')
da linha15
, coloca naCall Stack
e o executa, logo após o comando é removido da pilha; - Na linha
16
a funçãofuncao01()
é chamada e colocada naCall Stack
; - Ao executar a função
funcao01()
, encontramos na linha10
a chamada para a funçãobuscaDados()
, a mesma é colocada naCall Stack
; - Ao ler a função
buscaDados()
, o javascript encontra na linha4
o comandosetTimeout
, como ele é uma função assíncrona o javascript o coloca na filaWeb Apis
para ser executado, aguardará 3 segundos e após isso seu callbacksimulaConsultaApi()
é movido para aCallback Queue
, para aguardar seu momento para entrar naCall Stack
; - Nesse momento temos na
Call Stack
, do topo da fila para baixo, as seguintes funções:buscaDados
,funcao01
emain
; - Continuando no fluxo da
Call Stack
, a funçãobuscaDados
é removida da fila; - O fluxo continua na linha
11
, onde o comandoconsole.log('funcao01')
, é colocado naCall Stack
, e após a sua execução ele é removido da pilha; - A função
funcao01()
é removida daCall Stack
; - A função
main()
é removida daCall Stack
; - O fluxo segue na linha
20
, o comandoconsole.log('Fim!')
é colocado naCall Stack
e executado, logo após é removido; - Como a fila da
Call Stack
está vazia, oEvent Loop
irá pegar ocallback simulaConsultaApi()
e coloca-la naCall Stack
; - Ao executar o
callback simulaConsultaApi()
, o comandoconsole.log('Dados retornados')
da linha5
será posto naCall Stack
, executado e logo após removido. - E para finalizar, o
callback simulaConsultaApi()
será removido daCall 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 :)