“Node.js’s asynchronicity is quite literally one of the, if not the best part of Node.js”
That’s fine if that is your opinion. Here is my perspective on it:
- Node.js async model is not unique nor innovative. There has been several other platforms (such as Tcl/TK or 90s Windows programming). It runs into the same problem with any cooperative concurrency control, where a block of execution can freeze out everything else. In contrast, the Erlang/Elixir has a preemptive scheduler
- Nodejs’s async/await has an implicit queue of execution blocks. Erlang/Elixir, on the other hand, queues _messages_ recieved by a process. This allows not only transmission of data, but also control signals (such as suspend, stop, restart), which you cannot do with Nodejs promises alone. You have to use event emitters, whereas every Erlang/Elixir lightweight process can receive event messages.
- Nodejs promises can get lost. If it is not held and tracked somewhere, you can’t get to it. Erlang and Elixir has a first-class, PID literal. You can pull up any running process because the scheduler tracks them, which makes live debugging or mitigations possible in prod
- Because Nodejs queues execution instead of messages, it has to use callbacks. The problem with callbacks are not necessarily callback hell so much as tracking rejections and errors. Every single async call requires it, whereas in Erlang and Elixir, a running process is a natural error isolation boundary.
- Nodejs only occupies a single core of a processor, unless you use workers or fork. The BEAM runtime (these days) will use up as many of the cores as available unless you tell it not to. I can run a single BEAM os process, but have to jump extra hoops to take advantage of that for Nodejs.
Those are the main reasons why, from my perspective, Nodejs is error prone by design.