That's exactly the problem with node.js and other promise/callback/deferred/futures mechanism - it mixes concerns. You business algorithm probably doesn't look like "1) authenticate & authorize 2) return a callback then go to select loop to process 100 other connections 3) add book to cart etc". It probably doesn't have 2) in there, and it shouldn't. The point was that that particular request doesn't and shouldn't have anything else to do besides wait for the connection from database to return a result.
Sure there maybe there are 2 million other (https://blog.whatsapp.com/196/1-million-is-so-2011) connected clients wanting to buy a book, but the logic for a single doesn't have to care about it. That's the beauty of it.
> If you're blocking on something waiting for a result while you could do something useful in the meantime, that is the definition of hogging resources.
That's not something you should be doing that high up the abstraction levels. If you have to worry about it that high up there is something broken with the framework / language / library.
Like the OP says, it is basically the equivalent of Windows 3.1. Nobody sane today would put their crown jewel, latest production systems running on Windows 3.1 but yet we do with when it comes to our choice of frameworks and libraries.
In theory their performance and behavior should be the same. BEAM and other N:M green thread systems being maybe faster if they can efficiently use multiple threads. But since they use message passing anyway (needed for isolation), they are again almost equivalent to running multiple Node/reactive request handlers.
N:M is probably useful for desktop apps. But for that single thread is (should be) always enough, so same behavior.
This isn't about performance, it's about how you write code. In Erlang/Elixir (and also Go and Haskell), you aren't sitting there constantly explaining to the runtime over and over again how to plumb together bits of "async" code. Essentially, all code is simply presumed async, and the plumbing is always already there for you; the act of calling a function includes, for free, all the "async" plumbing.
I expect anyone who has been doing this for a couple of months should have noticed it's the same plumbing, over and over again. Here's the long-running IO task. Here's what to do with it when it's done. Here's what to do with errors. Here's the next long-running IO task. Here's what to do with it when it's done. Here's what to do with errors, which is pretty similar to the last thing. Here's the next long-running IO task. Here's what to do with it when it's done. Here's what to do with errors; whoops, forget that one, which is gonna cost me during debugging but nothing in the code, runtime, or syntax affordances particularly cares that I forgot. Here's the next long-running IO task....
Yes, they do come out roughly the same in speed for Erlang/Elixir and JS, and Go can quite easily be substantially faster... and easier to write.
And if we want to discuss performance, Go still isn't as fast as it could be. There's a solid reason why N:M threading can't be the fastest way to run code in general, but I don't see much reason why it couldn't be something more like 20% away from C speed in general, with some pathological cases where it's much slower and out of the question (very fast packet handling with those fancy software TCP cards, for instance).
From what I've done async/await in ES (and C#) is easier to write than Go's equivalent, but we're hitting your mileage may vary territory.
Yes, I see, if you have data crunching in the event loop, you have to "manage" it, by either delegating it outside of it, or chunk it up into small pieces, and yield between.
But that should result in the same performance as BEAM, because you can run 1 big runtime or many small event loops on each box, and put a load balancer in front (which is needed even for BEAM, because if you have a more threads/processes than cores throughput AND latency will suffer).
Agreed on the DoS thing, but nothing, sort of a proper timeout (and/or hard RT) will save you. Yes, with Node et al. you can lose a lot of requests when you inevitably kill the runtime. But it's just as easy to DoS a different runtime. (BEAM just gives up a few seconds later.)
I dunno, maybe you could serve one of your 100 other concurrent requests?
> You can do something useful in the meanwhile and get the result later
and then immediately...
> It has nothing to do with hogging or stealing resources.
It has everything to do with this. If you're blocking on something waiting for a result while you could do something useful in the meantime, that is the definition of hogging resources.