This article is not about bashing javascript. It has served us well over the years in the browser, and after that, it has continued to help us on the server. Given the widespread knowledge of javascript, it's understandable that many people also reach for it when implementing their backends.
CPU
The biggest flaw with javascript on the server is the fact that it's single-threaded. This means that it cannot seamlessly spread the work out to utilize all the cores the server has available. You as a developer will need to manually move some work out to the workers to enable javascript to make use of more cores, to get this to be as efficient as other platforms are almost impossible.
Your next option, which is the most commonly used is to spin up multiple instances of your application and that way enable the operating system to schedule your work on a different core than the one your other instances are working on. Generally, you spin up as many instances as you have cores in your system, that way you at least have a chance of using all of your CPU.
Web developers working in Rust, Go or .net do not have to worry about this, as the selected async runtime will handle this for them.
Memory
Another flaw that might hit you if you are not aware of it is that V8 (the javascript runtime that powers node) is limited to a heap size of 2Gb/4Gb (differs between versions). It doesn't matter how much ram you have in your machine, if you reach this heap size usage, your application will crash, which could be devastating if the crash is due to high usage and not a memory leak. The way to counter this is to set the max-old-space-size setting on your node process to a limit that you deem correct for your server.
Developers working in Rust, Go or .net don't have to worry about this, as there is no hidden application crashing memory limit.
Performance
Thanks to the competition over the years to get the fastest browser, we have gotten to a point where the performance of javascript can be pretty incredible. With that said, it's not guaranteed to be running at its peak performance. You see, javascript has two problems, first, it needs to have a fast "start from source"-time, then it needs to be able to run the actual application logic fast. This is solved by having a multi-step compilation process, where the first pass just gets the code up and running without spending much time on optimizations. After the application has been running for a while, V8 knows where its worth spending time applying optimizations and it will put its optimizing compiler on the task to do the optimizations.
Usually, this is fine, after a while when your node process has started up, it gets optimized and servers have historically been long-running processes. In the current days of serverless though, things are different. Sometimes code starts up to only do one pass through the code, this is not optimal for the V8 engine as it will never get a chance to apply its optimizations. With that said, it would be even worse if it did do its optimizations, as that would mean it spent lots of CPU cycles to optimize something that will be thrown away only milliseconds later.
A Rust developer doesn't have to worry about this, as the optimization happens at compile time, and when the application starts it runs at peak performance from the getgo.
I hope you found this informative, a๐ is always appreciated, thanks!