Aqui está a descrição do nó que você não ouviu antes. O objetivo do node.js, ou qualquer long-polling / websocket / qualquer servidor de rede realmente, é mapear conexões M para N núcleos físicos em sua caixa. Compreender os detalhes disso ajudará a deixar claro por que usar async em todos os lugares é insano.
Você já se perguntou por que JS é assíncrono? É assíncrono porque foi construído para ser uma linguagem de script de IU. Acontece que as IUs são muito semelhantes aos servidores de longa pesquisa, pois ambos têm um grande número de fontes de eventos (elementos da IU no caso de um navegador, soquetes no caso de um servidor), que devem ser multiplexados em um sistema não bloqueador fio. Isso é M: 1, onde M é o número de conexões (ou elementos de IU) e 1 é o único thread de uma plataforma JS.
Escrever código assíncrono de thread único que funcione bem é difícil. Em primeiro lugar, o código assíncrono é mais detalhado e mais difícil de ler do que o código de bloqueio equivalente. Além disso, você tem um único thread que não pode bloquear. Tente processar 3.000 linhas de banco de dados no nó e observe seu QoS sofrer. Você sempre pode contornar isso, mas a solução é uma merda. Usar nextTick adiciona complexidade desnecessária, e um web worker é bom, mas se você está realmente girando em torno de threads, por que não está usando outra linguagem que pelo menos ofereça a conveniência de um código não assíncrono, ou mesmo salvando isso, por que não Você não está usando uma linguagem como o java, que vem empacotada com incrível suporte a threading por meio de sua estrutura de executores e classes atômicas?
O agendamento de threads é incrível e funciona bem, ao contrário do que muitos querem que você acredite. Funciona especialmente bem para aplicativos da web em que os threads geralmente não disputam recursos no próprio servidor de aplicativos. Os aplicativos da Web geralmente são processos independentes. O problema de memória compartilhada geralmente é mínimo em um aplicativo da web com arquitetura adequada.
A questão é que um reator assíncrono como o nó é um padrão fantástico para lidar com IO e um padrão terrível para lidar com lógica de aplicativo. É fantástico para IO porque um único thread pode realmente ser mais rápido devido à falta de interações de simultaneidade (bloqueios, agendamento de thread, etc.) ao fazer muitas operações rápidas sem bloqueio.
A realidade da lógica aplicativo é que a lógica aplicativo faz bloco realizando ops paralelas porque as operações de CPU são, em certo sentido, bloqueando. Você ainda está lutando por uma série de núcleos. Nesse caso, o que você deseja é M: N, onde M é o número de conexões e N é o número de threads ativos que manipulam a lógica do aplicativo. Eu digo deixe async fazer o que faz bem (IO paralelo) e deixe os threads fazerem o que fazem bem (agendar CPU).
Algumas pessoas podem se perguntar como isso funciona na prática, pensando que 1000 conexões de websocket precisam de 1000 threads. O que você deve ter é 1 reator assíncrono manipulando o IO entregando mensagens discretas para N threads. É um bom padrão e funciona bem. O estado pode ser encapsulado em um closure ou um objeto, isso é com você (e sua linguagem de programação).
No caso de IO altamente paralelo, sim , mil vezes sim assíncrono é ótimo. Mas a grande mentira sobre o nó é que as pessoas precisam transportar o assíncrono da camada IO para a camada lógica do aplicativo. No nó, está tudo misturado.
O truque sobre o assíncrono é que seu servidor pode lidar com 10.000 conexões que estão ociosas, mas apenas algumas ativas em um determinado momento, já que você tem apenas alguns núcleos. O agendamento de threads funciona bem.
Nenhuma dessas ideias é nova; na verdade, elas têm décadas . Só posso esperar que as pessoas estejam dispostas a abrir suas mentes para a ideia de que o futuro da programação concorrente da web tem muitas possibilidades.