O que é E / S não-bloqueadora ou assíncrona no Node.js?

136

No contexto dos mecanismos Javascript do lado do servidor, o que é E / S sem bloqueio ou E / S assíncrona? Vejo isso ser mencionado como uma vantagem sobre as implementações do lado do servidor Java.

Anand
fonte
3
É útil pensar em tags de script no ambiente do navegador para entender esse conceito. Zakas tem um ótimo artigo sobre isso - as primeiras seções devem ser suficientes para explicar o conceito de bloqueio: nczonline.net/blog/2010/08/10/what-is-a-non-blocking-script
netpoetica

Respostas:

317

Síncrono vs Assíncrono

A execução síncrona geralmente se refere à execução de código em sequência. A execução assíncrona refere-se à execução que não é executada na sequência em que aparece no código. No exemplo a seguir, a operação síncrona faz com que os alertas sejam acionados em sequência. Na operação assíncrona, embora alert(2)pareça executar o segundo, isso não ocorre.

Síncrono: 1,2,3

alert(1);
alert(2);
alert(3);

Assíncrono: 1,3,2

alert(1);
setTimeout(() => alert(2), 0);
alert(3);

Bloqueio vs Não bloqueio

Bloqueio refere-se a operações que bloqueiam a execução adicional até que a operação seja concluída. Não-bloqueio refere-se ao código que não bloqueia a execução. No exemplo dado, localStorageé uma operação de bloqueio, pois interrompe a execução para leitura. Por outro lado, fetché uma operação sem bloqueio, pois não paralisa a alert(3)execução.

// Blocking: 1,... 2
alert(1);
var value = localStorage.getItem('foo');
alert(2);

// Non-blocking: 1, 3,... 2
alert(1);
fetch('example.com').then(() => alert(2));
alert(3);

Vantagens

Uma vantagem de operações assíncronas sem bloqueio é que você pode maximizar o uso de uma única CPU e também de memória.

Exemplo de bloqueio síncrono

Um exemplo de operações de bloqueio síncrono é como alguns servidores da Web, como os de Java ou PHP, lidam com solicitações de IO ou de rede. Se o seu código lê de um arquivo ou do banco de dados, seu código "bloqueia" tudo após a execução. Nesse período, sua máquina está mantendo a memória e o tempo de processamento de um thread que não está fazendo nada .

Para atender a outras solicitações enquanto o encadeamento está parado, depende do seu software. O que a maioria dos softwares de servidor faz é gerar mais threads para atender a solicitações adicionais. Isso requer mais memória consumida e mais processamento.

Exemplo assíncrono e sem bloqueio

Servidores assíncronos e sem bloqueio - como os criados no Node - usam apenas um thread para atender a todas as solicitações. Isso significa que uma instância do Node tira o máximo proveito de um único thread. Os criadores o projetaram com a premissa de que as operações de E / S e de rede são o gargalo.

Quando as solicitações chegam ao servidor, elas são atendidas uma de cada vez. No entanto, quando o código atendido precisa consultar o banco de dados, por exemplo, ele envia o retorno de chamada para uma segunda fila e o thread principal continua em execução (não espera). Agora, quando a operação do banco de dados é concluída e retorna, o retorno de chamada correspondente é retirado da segunda fila e enfileirado em uma terceira fila na qual eles estão aguardando execução. Quando o mecanismo tem a chance de executar outra coisa (como quando a pilha de execução é esvaziada), ele pega um retorno de chamada da terceira fila e o executa.

Joseph
fonte
5
Não sei se entendi o seu segundo parágrafo em Bloqueio de PHP . Você está dizendo que, "Embora o PHP normalmente bloqueie no IO, isso não ocorre porque o sistema operacional encadeia automaticamente as operações de IO". Ou você está dizendo que isso não é um problema no PHP, porque o PHP cria automaticamente um novo encadeamento para cada solicitação, para que uma solicitação bloqueada não interrompa todo o ambiente PHP? (Eu estou supondo o último ..)
dcow
6
É o último.
Joseph
2
espere, se for o último, quais são as vantagens de não bloquear E / S PHP (como reactPHP ou outra coisa) sobre o bloqueio. ainda confuso
Sunu Pinasthika Fajar
5
@CharlieParker Sim. A operação assíncrona é paralela ao seu código. Mas o retorno de chamada que "retorna" aos resultados da operação assíncrona é colocado na fila para execução no código principal quando o código principal não está ocupado.
Joseph
2
@CharlieParker Aqui está um post que trata mais sobre os elementos internos do mecanismo assíncrono.
Joseph
7
var startTime = new Date().getTime();
var getEndTime = () => {
    var tempEndTime = new Date().getTime();
    var second = (tempEndTime - startTime)/1000
    return `took ${second} sec...to finish\n`
}

console.log('1: start App', getEndTime())
setTimeout(()=>{
    console.log('2: setTimeout', getEndTime())
}, 1000)
console.log('3: End App', getEndTime())

// console -> Process Order:  1 -> 3 -> 2

Exemplo de código

Wayne Chiu
fonte