‫Instrutor: Bem-vindo de volta.
‫Agora vamos aprender tudo sobre o loop de eventos, que
‫é o coração do Nó. arquitetura js.
‫E esta é provavelmente a palestra
‫mais importante desta seção, então certifique-se de realmente
‫entender tudo o que eu mostro durante este vídeo.
‫Então vamos começar.
‫Então, aqui está um diagrama semelhante à última
‫aula para que saibamos exatamente do que estamos falando aqui.
‫Portanto, ainda estamos em um processo Node na única thread em
‫que o loop de eventos é executado, certo?
‫Agora, a primeira coisa que você
‫precisa saber é que o loop de evento é
‫onde todo o código do
‫aplicativo que está dentro das funções de retorno de chamada é executado.
‫Portanto, basicamente, todo código que não seja um código de nível superior será
‫executado no loop de eventos.
‫Algumas partes podem ser descarregadas para o pool de threads
‫como vimos na última aula,
‫mas é o loop de eventos que cuida de tudo isso.
‫Como eu disse antes, é realmente o coração
‫da arquitetura do Node.
‫Ok, agora, como mencionei muitas vezes na primeira parte
‫do curso, Node. js é todo construído em
‫torno de funções de retorno de chamada.
‫Assim, funções que são chamadas assim
‫que algum trabalho é concluído em algum momento no futuro.
‫Lembre-se disso?
‫E funciona assim porque o Node
‫usa uma arquitetura acionada por evento, que
‫é algo sobre o qual falaremos em um
‫dos próximos vídeos.
‫Mas o que você precisa saber
‫por enquanto é que coisas
‫como nosso aplicativo recebendo uma solicitação HTTP em nosso servidor ou um temporizador expirando
‫ou um arquivo acabando de ser lido, tudo isso emitirá eventos
‫assim que terminar seu trabalho, e nosso
‫evento loop irá então pegar esses eventos e
‫chamar as funções
‫de retorno de chamada associadas a cada evento.
‫Ok, faz sentido?
‫Portanto, novamente, o loop de eventos recebe eventos
‫cada vez que algo importante acontece
‫e, então, chamará os callbacks necessários, como
‫definimos em nosso código.
‫Portanto, em resumo, costuma-se dizer que
‫o loop de eventos faz a orquestração, o
‫que significa simplesmente que ele recebe eventos, chama
‫suas funções de retorno de
‫chamada e descarrega as tarefas mais caras para o pool de threads.
‫Agora, como tudo isso realmente funciona nos bastidores?
‫Em que ordem esses callbacks são executados?
‫Bem, é isso que vamos descobrir a seguir.
‫Portanto, lembre-se, quando iniciamos nosso aplicativo Node, o loop
‫de eventos começa a ser executado imediatamente.
‫Agora, o loop de eventos tem
‫várias fases, e cada fase tem uma
‫fila de callback, que são os callbacks provenientes
‫dos eventos que o
‫loop de eventos recebe, conforme falamos no último slide.
‫Agora, em alguns lugares você vai ler
‫que existe apenas uma fila de retorno de chamada ou uma fila de eventos,
‫mas na verdade, como eu disse, o loop de eventos tem muitas fases em
‫que cada fase tem sua própria fila de retorno de chamada.
‫Portanto, vamos agora dar uma olhada nas quatro fases mais importantes.
‫Existem uma ou duas outras fases que
‫são usadas internamente pelo Node,
‫mas não são tão
‫importantes e não vou falar sobre elas.
‫Portanto, a primeira
‫fase cuida de callbacks de temporizadores
‫expirados, por exemplo, da função setTimeout ().
‫Portanto, se houver funções de retorno de chamada de
‫temporizadores que acabaram de expirar, essas são as primeiras
‫a serem processadas pelo loop de eventos.
‫Se um temporizador expirar mais tarde durante o tempo em
‫que uma das outras fases estiver
‫sendo processada, bem, o retorno de chamada desse temporizador só será
‫chamado assim que o loop de evento voltar a esta primeira fase.
‫Faz sentido?
‫E funciona assim em todas as quatro fases.
‫Assim, os callbacks em cada fila são
‫processados um a um até que não haja mais nenhum na
‫fila, e só então o loop de eventos entrará na próxima fase.
‫Em seguida, temos o polling de I / O e a execução de callbacks de I / O.
‫E, novamente, lembre-se de que I / O significa entrada / saída.
‫Portanto, a pesquisa basicamente significa procurar novos eventos de E /
‫S que estão prontos para serem
‫processados e colocá-los na fila de retorno de chamada.
‫E lembre-se de que, no contexto de um aplicativo
‫Node, I / O significa principalmente coisas como rede e acesso a
‫arquivos e, portanto, é nesta fase que provavelmente 99%
‫do nosso código é
‫executado, simplesmente porque em um aplicativo
‫Node típico, a maior parte do que precisamos fazer está relacionado
‫à rede e também, acesso a arquivos.
‫A próxima fase é para retornos de chamada setImmediate e setImmediate
‫é um tipo especial de cronômetro que podemos
‫usar se quisermos processar retornos de chamada imediatamente após a pesquisa de
‫E / S e a fase de execução, o que pode
‫ser importante em alguns casos de uso mais avançados.
‫Tudo bem.
‫E, por fim, a quarta fase é para retornos de chamada fechados, que,
‫novamente, não são tão importantes para nós, mas
‫coloquei isso aqui de qualquer maneira por uma questão de integridade.
‫Basicamente, nesta fase, todos os eventos de fechamento são processados,
‫por exemplo, para o encerramento de um servidor web ou de um WebSocket.
‫Portanto, essas são as quatro fases do loop de
‫eventos, mas além dessas quatro filas de retorno de chamada
‫que acabamos de ver, há também duas
‫outras filas, a fila nextTick () e a outra
‫fila de microtarefas, que é principalmente para promessas resolvidas.
‫Se você não estiver familiarizado
‫com as promessas, falaremos sobre elas um pouco mais adiante.
‫De qualquer forma, se houver callbacks em
‫uma dessas duas filas para serem
‫processados, eles serão executados logo após o término da fase
‫atual do loop de eventos, em vez de
‫esperar que todo o loop termine.
‫OK?
‫Ou seja, após cada uma dessas quatro
‫fases, se houver callbacks nessas duas filas especiais,
‫eles serão executados imediatamente.
‫Agora, por exemplo, imagine que uma promessa resolve e retorna
‫alguns dados de uma chamada de API enquanto o retorno de
‫chamada de um cronômetro expirado está em execução.
‫Portanto, neste caso, o retorno de chamada da promessa será
‫executado logo após o término daquele do temporizador.
‫OK?
‫E a mesma lógica também se aplica à fila nextTick (),
‫sobre a qual ainda não falamos.
‫Então, basicamente, processar o nextTick () é uma função que podemos
‫usar quando realmente, realmente precisamos executar um determinado
‫retorno de chamada logo após
‫a fase de loop de evento atual.
‫É um pouco semelhante a setImmediate, com a diferença de
‫que setImmediate só é executado após a fase de retorno
‫de chamada de E / S.
‫O que é
‫semelhante, porém, é que ambos são para casos de uso
‫realmente avançados e provavelmente nem precisaremos deles
‫ao longo deste curso.
‫Mas de qualquer forma, eu queria incluir essas coisas mais
‫complexas aqui também para que você tenha
‫as ferramentas de que precisa se realmente
‫precisar se aprofundar no Node. js se você quiser.
‫Tudo bem, e com isso, na verdade terminamos um
‫tick do loop de eventos, e um tick é basicamente apenas um
‫ciclo neste loop.
‫Portanto, agora é hora de decidir se o loop deve
‫continuar para o próximo tick ou se o programa deve ser encerrado.
‫E como o Node faz isso?
‫Bem, é muito simples.
‫O Node simplesmente verifica se há algum
‫temporizador ou tarefa de E / S ainda em execução
‫em segundo plano e, se não houver, ele sairá do aplicativo.
‫Mas se houver algum temporizador ou tarefa de E / S
‫pendentes, bem, ele continuará executando o loop
‫de eventos e irá direto para o próximo tick.
‫Então, por exemplo, quando ouvimos
‫as solicitações HTTP de entrada como fizemos em nosso
‫projeto de farm de Nó na seção anterior, estávamos basicamente executando
‫uma tarefa de E / S e é por isso
‫que o loop de evento e,
‫portanto, o Nó. js, continue
‫executando e escutando as novas solicitações de HTTP que
‫chegam, em vez de apenas sair do aplicativo.
‫Além disso, quando estamos escrevendo ou lendo um
‫arquivo em segundo plano, isso também é uma tarefa de
‫E / S e, portanto, faz sentido que o
‫aplicativo não saia enquanto estiver trabalhando com aquele arquivo, certo?
‫Ok, e isso é basicamente o que você deve saber
‫sobre o Node. loop de eventos js.
‫Se precisar de mais detalhes do que isso,
‫bem, você sempre pode
‫tentar ler a documentação oficial do Node, que deve ser bem
‫fácil de entender neste ponto, agora que você
‫já entende a maior parte do loop de eventos de qualquer maneira.
‫E eu só quero enfatizar que
‫é muito, muito importante que você entenda corretamente o loop
‫de eventos para que possa escrever seu
‫próprio código de desempenho e também depurar seu próprio código
‫quando algo der errado de uma maneira inesperada.
‫E agora, para terminar, vamos revisar algumas das
‫coisas sobre as quais falamos aqui.
‫Então, em poucas palavras, a coisa mais importante que eu
‫quero que você entenda desta palestra, e talvez
‫de todo o curso, é que o loop
‫de eventos é o que torna a
‫programação assíncrona possível no Node. js, tornando-o o
‫recurso mais importante no projeto e na criação
‫do Node. js completamente
‫diferente de outras plataformas.
‫Ele cuida de todos os eventos de
‫entrada e realiza a orquestração transferindo tarefas mais pesadas para o
‫pool de threads e fazendo o trabalho mais simples por conta própria.
‫Além disso, lembre-se de que precisamos do loop
‫de eventos porque em Node. js tudo funciona em um
‫único thread, e assim, você pode
‫ter milhares ou milhões de usuários acessando o mesmo thread ao mesmo tempo.
‫Isso torna o Node tão leve e escalável,
‫mas ao mesmo tempo, vem com o perigo
‫de bloquear nosso único
‫thread, o que tornaria o aplicativo
‫inteiro lento ou até mesmo pararia para todos os seus usuários acessarem o aplicativo, certo?
‫Agora, em outras linguagens como PHP em
‫execução em um servidor Apache, basicamente, um novo thread
‫é criado para cada novo usuário, o que consome muito mais recursos.
‫Mas por outro lado, não
‫há perigo de bloqueio, certo?
‫Então, todo esse modelo torna um pouco
‫mais fácil usar o PHP para iniciantes, mas é
‫claro, ele vem com suas próprias
‫desvantagens, que não vou abordar neste ponto.
‫De qualquer forma, deixe-me
‫lembrá-lo novamente de que é sua responsabilidade não bloquear o loop de
‫eventos e, portanto, aqui estão algumas diretrizes para isso.
‫Em primeiro lugar, não use as
‫versões de sincronização de funções nos módulos fs,
‫crypto ou zlib em suas funções de retorno de chamada, certo?
‫Então, em nosso primeiro projeto, nós realmente usamos a
‫versão síncrona, mas ela estava no código de nível superior, portanto,
‫fora de qualquer retorno de chamada.
‫E como esse código é executado antes mesmo do loop
‫de eventos começar, bem, não é problema usar a versão síncrona aí.
‫Além disso, e isso provavelmente é bastante
‫óbvio, não execute cálculos muito complexos no loop de eventos.
‫Então, coisas como processar
‫milhões de números em loops dentro de loops, ou algo assim.
‫Em seguida, tome cuidado com o JSON em objetos muito
‫grandes porque, em algum ponto, pode começar
‫a demorar muito para analisar ou sequenciar o JSON.
‫E, finalmente, não use expressões regulares muito complexas, por
‫exemplo, com vários quantificadores aninhados ou
‫referências anteriores, porque, novamente, eles
‫podem demorar mais do que o esperado.
‫Essas são, é claro, apenas algumas
‫diretrizes de alto nível, mas elas o ajudarão a começar
‫no caminho certo.
‫Agora, existem algumas soluções potenciais
‫para esses problemas de bloqueio, como descarregar manualmente para
‫o pool de threads ou usar processos filhos, e podemos falar
‫sobre isso no final do curso ou
‫em algum momento no futuro, mas por
‫agora, é importante que você entende e segue este conselho
‫de realmente não bloquear o loop de eventos.
‫Tudo bem.
‫A seguir, vou dar um pequeno exemplo para
‫mostrar algumas das
‫coisas que falamos na prática.