Perspetiva Geral do Conceito de Bloqueante vs Não Bloqueante

Autores do Artigo
Tabela de Conteúdos

Esta visão de conjunto cobre a diferença entre as chamadas bloqueantes e não bloqueantes na Node.js. Esta visão geral referir-se-á ao laço de evento e libuv mas nenhum conhecimento prévio destes tópicos não é obrigatório. Assume-se de que os leitores têm um entendimento básico do padrão de função de resposta da linguagem de JavaScript e da plataforma da Node.js.

"I/O" (Input/Output por extenso em Inglês, Entrada/Saída em Português) refere-se primariamente à interação com o disco e a rede do sistema suportada pela libuv.

Bloqueante

Bloqueante é quando a execução do código de JavaScript adicional no processo da Node.js deve esperar até uma operação que não é de JavaScript terminar. Isto acontece porque o laço de evento é incapaz para continuar a execução do código de JavaScript enquanto uma operação bloqueante está acontecendo.

Na Node.js, o código de JavaScript que apresenta pobreza de desempenho por ser intenso em CPU no lugar de esperar uma operação que não é de JavaScript, tais como I/O, normalmente não são remetidas como bloqueante. Os métodos síncronos na biblioteca padrão da Node.js que usam a libuv são as operações bloqueantes mais comummente usadas. Os módulos nativos também podem ter métodos bloqueantes.

Todos os métodos de I/O na biblioteca padrão da Node.js fornecem versões assíncronas, que são não bloqueantes, e aceitam funções de resposta. Alguns métodos também têm equivalentes bloqueantes, que têm nomes que terminam com Sync.

Comparando Código

Os métodos bloqueantes executam de maneira síncrona e o métodos não bloqueantes executam de maneira assíncrona.

Usando o módulo de Sistema de Ficheiro como exemplo, este é uma leitura de ficheiro síncrona:

E aqui está um exemplo assíncrono equivalente:

O primeiro exemplo parecesse com o segundo mais tem a desvantagem da segunda linha bloqueando a execução de qualquer código adicional de JavaScript até o ficheiro inteiro ser lido. Nota que na versão síncrona se um erro for lançado, precisará ser capturado ou o processo rebentará. Na versão assíncrona, está sobre a responsabilidade do autor decidir se um erro deve ser lançado como mostrado.

Vamos expandir um pouco o nosso exemplo:

E aqui está um similar, mas não um exemplo assíncrono equivalente:

No primeiro exemplo acima, o console.log será chamado antes da chamada da função moreWork(). No segundo exemplo fs.readFile() é não bloqueante então execução do código de JavaScript pode continuar e moreWork() será chamado primeiro. A capacidade de executar moreWork() sem ter de esperar que a leitura do ficheiro seja concluída é a escolha de desenho chave que permite alta produtividade.

Concorrência e Produtividade

A execução de código de JavaScript na Node.js acontece em uma única linha de processamento, assim concorrência refere-se a capacidade do laço de evento de executar funções de resposta da JavaScript depois de completar outra atividade. Qualquer código que é esperado ser executado em simultâneo devem permitir o laço de evento continuar a execução enquanto operações que são de JavaScript, como I/O, são acontecendo.

Como exemplo, vamos considerar um caso onde cada requisição para um servidor da web demora 50ms para completar e 45ms deste 50ms é I/O de base de dados que podem ser feitas de maneira assíncrona. Escolher operações assíncronas não bloqueantes libera aqueles 45ms por requisição para lidar com outras requisições. Isto é uma diferença significativa em capacidade apenas por escolher usar métodos não bloqueante ao invés de métodos bloqueante.

O laço de evento é diferente dos modelos usados em muitas outras linguagens onde linhas de processamentos adicionais podem ser criadas para lidar com atividade simultânea.

Perigos de Misturar Código Bloqueante e Não Bloqueante

Existem alguns padrões que devem ser evitados quando lidamos com I/O. Vamos ver um exemplo:

No exemplo acima, fs.unlinkSync() provavelmente será executado antes do fs.readFile(), que eliminaria o ficheiro file.md antes dele ser de fato lido. Uma maneira melhor de escrever isto, que é completamente não bloqueante e garantido que executará na ordem correta é:

No exemplo acima coloca uma chamada não bloqueante para fs.unlink() dentro da função de resposta do fs.readFile() o que garante a ordem correta das operações.

Recursos Adicionais