Introdução

Node.js é um tempo de execução JavaScript de plataforma cruzada de código aberto desenvolvido no mecanismo Chrome V8. O tempo de execução é a fase final do ciclo de vida do programa durante a qual o programa é executado enquanto a máquina executa o código do programa.

Para que o Node seja eficaz em tempo de execução, ele usa um modelo de E/S não bloqueado e orientado por eventos que o torna leve e eficiente, perfeito para aplicativos em tempo real com grande quantidade de dados que são executados em dispositivos distribuídos. Neste artigo, examinamos as diferentes maneiras pelas quais a máquina executa o código.

Async, simultaneidade e paralelismo explicados

O Node.js usa um padrão de design assíncrono orientado a eventos, o que significa que várias ações são realizadas ao mesmo tempo durante a execução de um programa. Por exemplo, se houver dois terminais que se seguem no programa, o servidor moverá um para fazer uma solicitação ao segundo terminal sem esperar que o primeiro retorne os dados.

Tudo isso é possível graças ao mecanismo de eventos do Node, que ajuda o servidor a obter uma resposta da chamada API anterior; este é o modelo de programação assíncrona em poucas palavras.

Na outra extremidade do espectro está a programação síncrona, onde uma única ação é realizada por vez. Isso significa que, como uma tarefa está esperando por uma resposta, você não pode chamar outra, pois a execução de todas as outras partes do programa está bloqueada.

Simultaneidade significa que um programa é capaz de executar mais de uma tarefa ao mesmo tempo-isso não deve ser confundido com paralelismo. Durante a simultaneidade, diferentes tarefas com objetivos diferentes do programa podem ser realizadas ao mesmo tempo, enquanto no paralelismo, diferentes partes do programa executam uma tarefa.

A simultaneidade requer a alocação adequada de recursos para funcionar de forma eficiente, enquanto o paralelismo envolve partes quebradas do programa trabalhando de forma independente para realizar a mesma tarefa. Um exemplo de simultaneidade é o fato de que você pode baixar uma imagem enquanto pode postar uma resposta na mesma imagem.

O paralelismo, por outro lado, é obtido por meio do uso da API Web Workers . Os Web Workers são provavelmente a única maneira de obter um multiprocessamento “verdadeiro” em JavaScript. Dizemos “verdadeiro” aqui porque com setInterval () , setTimeOut () , XMLHttpRequest , async/await , e manipuladores de eventos, podemos imitar o paralelismo.

Inferno de callback, promessas e manipulação de execução assíncrona em Node.js

Em JavaScript síncrono, o programa executa uma tarefa por vez. Isso significa que todos os outros processos são colocados em espera, o que resulta em muito tempo entre os processos. Em JavaScript assíncrono, mais de uma tarefa será executada por vez, e isso cria retornos de chamada assíncronos.

Callbacks explicados

Callbacks assíncronos são funções especificadas como argumentos ao chamar uma função, que iniciará a execução do código em segundo plano. Quando o código de segundo plano termina de ser executado, ele chama a função de retorno de chamada para informá-lo de que a tarefa foi concluída. Um exemplo é addEventListener () :

 btn.addEventListener ('click', ()=> { alert ('Botão clicado!'); let pElement=document.createElement ('p'); pElement.textContent='Olá, caro amigo'; document.body.appendChild (pElem);
});

O primeiro parâmetro é o tipo de evento a ser ouvido e o segundo parâmetro é uma função de retorno de chamada que é invocada quando o evento é disparado, ou seja, a função de retorno de chamada não é executada imediatamente. Ele é “chamado de volta” mais tarde de forma assíncrona em algum lugar dentro do corpo da função que o contém.

A função de conteúdo é responsável por executar a função de retorno de chamada quando chegar a hora. Em vez de retornar imediatamente algum resultado, como a maioria das funções, as funções que usam callbacks levam algum tempo para produzir um resultado.

JavaScript assíncrono usando callbacks não é muito intuitivo e pode ser difícil de acertar. Aqui está um exemplo clássico do chamado inferno de retorno de chamada:

 fs.readdir (fonte, função (err, arquivos) { if (err) { console.log ('Erro ao encontrar arquivos:'+ err) } outro { files.forEach (function (filename, fileIndex) { console.log (nome do arquivo) gm (fonte + nome do arquivo).size (função (errar, valores) { if (err) { console.log ('Erro ao identificar o tamanho do arquivo:'+ err) } outro { console.log (nome do arquivo +':'+ valores) aspecto=(valores. largura/valores. altura) widths.forEach (function (width, widthIndex) { altura=Math.round (largura/aspecto) console.log ('redimensionar'+ nome do arquivo +'para'+ altura +'x'+ altura) this.resize (largura, altura).write (dest +'w'+ largura +'_'+ nome do arquivo, função (err) { if (err) console.log ('Erro ao gravar arquivo:'+ err) }) }.bind (this)) } }) }) }
})

Um dos motivos pelos quais isso acontece é porque os programadores tendem a escrever código de forma que a execução aconteça visualmente de cima para baixo.

Promessas de resgate

As promessas ajudam a tornar situações como o caos acima muito mais fáceis de escrever, analisar e executar. Uma promessa retornará um valor em algum ponto no futuro de um programa. Por exemplo, por motivos de segurança e privacidade, sempre que você faz uma vídeo chamada, a pessoa que está recebendo deve conceder acesso ao vídeo para que a chamada seja realizada:

 function phoneCallButton (evt) { setStatusMessage ("Tocando..."); navigator.mediaDevices.getUserMedia ({video: true, audio: true}) .então (chatStream=> { selfViewElem.srcObject=chatStream; callStream.getTracks (). forEach (track=> myPeerConnection.addTrack (track, callStream)); setStatusMessage ("Conectado ao usuário"); }). catch (err=> { setStatusMessage ("Falha ao conectar"); });
}

Quando a função é chamada, a mensagem de status "Tocando…" será enviada. Depois disso, getUserMedia será chamado em vez de esperar pelo usuário, habilitando os dispositivos escolhidos e retornando diretamente o MediaStream para o fluxo criado a partir das fontes selecionadas. getUserMedia () retorna uma promessa, que é resolvida com o MediaStream assim que estiver disponível.

Basicamente, enquanto o aplicativo não souber que a conexão foi estabelecida, o telefone continuará tocando. Apenas quando souber se o telefone foi conectado ou se o bloco catch foi chamado, ele irá parar.

.then () recebe uma função com um argumento, que é o valor resolvido de nossa promessa. .catch retorna o valor rejeitado de nossa promessa. .then () também fornece uma espécie de enfileiramento de eventos, que garante que não haja retorno de chamada incorreto.

Também é importante observar as funções async/await quando se trata de promessas. As funções assíncronas são declaradas com a palavra-chave async . As palavras-chave async e await permitem que o comportamento assíncrono baseado em promessa seja escrito em um estilo mais limpo, evitando a necessidade de configurar explicitamente as cadeias de promessa.

 app.post ('/user/signin', async (request, reply)=> { const data=request.body; resultado const=espera signinUser (dados); reply.status (200).send ({ status: 200, dados: resultado, }); });

No endpoint de login acima, para obter o resultado, a função aguarda os dados. As funções assíncronas podem conter zero ou mais expressões await . As expressões de espera suspendem o progresso por meio de uma função assíncrona e, em seguida, retomam o progresso apenas quando uma operação assíncrona baseada em promessa esperada for cumprida ou rejeitada.

Conclusão

Existem várias maneiras de lidar com operações assíncronas em paralelo no Node.js. Em primeiro lugar, você pode usar promessas, conforme discutido acima.

Você também pode usar uma biblioteca de terceiros, como async pacote npm , que garantirá que você não acabe causando um inferno de callback. Você pode instalá-lo usando o seguinte comando:

 npm install async--save

A postagem Paralelismo, simultaneidade e programação assíncrona em Node.js apareceu primeiro no LogRocket Blog .

Source link