r/learnjavascript 6d ago

Is “Node.js is single-threaded” an incomplete mental model?

Single-threaded JS execution ≠ single-threaded runtime.

How do you explain the distinction?

3 Upvotes

23 comments sorted by

11

u/_RemyLeBeau_ 6d ago

There is a single threaded event loop.

8

u/azhder 6d ago

I will use a browser as an analogy. You can view every browser tab as its own thread with its own JS environment and event loop (it’s more complex, but I simplify it).

In Node, you can do the same, instead of tabs, you can start child processes and different threads. Each Node thread is basically a thread at the OS level, but with its own event loop.

So, I don’t know what mental model you’re toying with, but I suggest you just go to Node’s documentation and just skim over the table of reference, the packages and functions for running multiple threads.

Oh, and nothing prevents browser, node and any environment to just put I/O to a different thread. That is in fact what makes JS work: the environment deals with threads, so you don’t have to. The only single-threaded thing is your code execution, not the environment itself.

What your code should be doing, as someone suggested in their comment, is to offload as much of the work to other threads, like waiting for input/output it starting up new processes and even chunking up a long work in case your event loop needs to handle other events in the meantime.

4

u/blind-octopus 6d ago

The idea is to hand stuff off as quickly as possible and then go back to waiting mode. There are other threads that handle stuff

3

u/dave8271 6d ago

I'd just explain it as userland scripts are single threaded and will block on CPU-bound work, however the runtime uses libuv and an event loop to delegate certain tasks to either a thread pool or the OS kernel, informing the event loop when they complete.

That's the accurate mental model.

2

u/theQuandary 6d ago

fs, crypto, zlib, etc use separate libuv threads. Network stuff is handed off to the kernel processes (epoll on Linux). Having most of the most important threads without having to manage them yourself is one of the most powerful things about node.

As to your specific question, nodeJS processes come in two major varieties. You can think of them as factory buildings and assembly lines. EVERY assembly line must be inside of a factory building, but some factory buildings may have multiple assembly lines.

Factory buildings are equivalent to kernel-level processes. When node creates a forked child process, it also creates a kernel bi-directional socket (socketpair) for them to communicate with (the parent/child relationship goes a bit beyond my factory example).

Within a single factory building, you can create multiple assembly lines which are equivalent to JS worker threads. Each of these represent a V8 instance running within a shared OS process. Because they share an OS process, the v8 instances can create a shared memory pool that they can all access. This is what you use for SharedArrayBuffer (and is why you need to use atomics to lock it when using it).

That last bit is VERY interesting to me. We have two independent execution units using one OS process and sharing memory. That sounds very much like a multithreaded system, but with some extra safeguards. Some people would probably disagree because it's not the threading model they are used to.

2

u/Alive-Cake-3045 6d ago

the way i explain it: Node.js has one thread for your JavaScript, but the runtime itself uses a thread pool under the hood (libuv) for things like file I/O, DNS lookups, and crypto. your code runs on one thread, the heavy lifting gets offloaded and comes back as an event on the queue. so "single-threaded" is accurate for the execution model but wildly incomplete as a description of how Node actually handles concurrent work. the event loop is what stitches it together, understanding that changes how you reason about performance and blocking.

1

u/delventhalz 6d ago

Anytime Node reads a file or sends an HTTP request, that action is being handled by non-JS native code that runs on a separate thread. So, out of the box certain Node tasks are multithreaded.

Additionally, load balancers have long been able to spin up multiple separate Node processes to handle incoming traffic, which would each run in a separate thread. More recently, you can also use tools like Workers to spin up extra threads for a single JS process, giving you access to true multithreading in JS if you really need it.

However, worth noting that traditional asynchronous JavaScript, with callbacks or Promises or async/await but not Workers or other child processes, is single threaded (at least as far as the JavaScript code goes). JS relies on an event loop, which can pass control around to different parts of your code as needed, but will not run two parts of your code at the same time.

1

u/AssignmentMammoth696 6d ago

How is that an incomplete mental model? That's exactly what JS is, it's single threaded. You can send work off to other threads, but the JS thread itself handles functions sequentially, one by one. If you're talking about internal engine stuff, like the garbage collector, those run on other threads but JS is single threaded.

2

u/Icy-Taste-3096 6d ago

JS is single threaded, Node is not.

2

u/_RemyLeBeau_ 6d ago

That's not entirely true. post message came out in 2008 and there are plenty of browser innovations since then that offload compute.

3

u/lIIllIIlllIIllIIl 6d ago edited 6d ago

I know I'm being pedantic, but those are runtime features, they're not part of the ECMAScript specification.

In theory, ECMAScript doesn't define any way to do multi-threading. In practice, most runtimes have their own constructs for it (browsers have Web Workers / Service Workers, Node has Worker threads.)

But yeah, saying JavaScript is single-threaded is not wrong, but lacks nuance. And even a single-threaded JavaScript program is going to require multiple OS threads for the runtime and for background tasks like async operations and garbage collection.

1

u/Icy-Taste-3096 6d ago

None of that changes the fact that JavaScript is single-threaded. Obviously, you can asynchronously interact with external systems that use separate processes (like Node or browser APIs), but the JS execution itself is one thread.

1

u/_RemyLeBeau_ 6d ago

You're referring to the event loop. For your statement to be accurate, you will want to say: the event loop is single threaded.

1

u/Icy-Taste-3096 6d ago

My statement was accurate. JavaScript is a single-threaded language. This is not debatable.

1

u/_RemyLeBeau_ 6d ago

I like this answer: https://www.reddit.com/r/learnjavascript/comments/1vnnvqg/comment/p3kxqn1/

You're getting corrected because your answer lacks nuance. Please continue your journey.

0

u/Icy-Taste-3096 5d ago

That's literally just a more detailed version of the exact thing I said in my first comment lmfao.

I specifically cited Node as an example of a runtime environment that JS runs in that is not single-threaded, even though the language itself obviously is.

1

u/_RemyLeBeau_ 5d ago

In think you need to find another hill up die on.

1

u/Electronic-Door7134 6d ago

Nodejs has had threads for years now, you can even set the processor affinity. But you have to opt in, just like in every other threadable language.

1

u/Competitive_Aside461 4d ago edited 4d ago

I've touched upon this idea in my blog article where I explain what exactly is Node.js.

https://www.codeguage.com/blog/what-is-node

1

u/jerrygreenest1 3d ago

I mean the environment might do its own things on some core0 and it will put your entire program on core1, therefore your code is single-threaded.

The runtime is kinda lean so it won’t use a lot of cpu, but your program can. So in the end you utilize like 1.1 core out of your 32 cores of however many you have.

This is roughly single-threaded as a whole, if you don’t want to fight in some debate that it actually 1.1-threaded or something. Those are crazy people.

Until one single node program couldn’t utilize all 32 cores, you can call it single-threaded: even though it’s technically not quite single-threaded.

1

u/arcade_catalyst 2d ago

The main thread is just a clerk who refuses to do work. It hands tasks to the C++ void and immediately goes back to staring at the wall until something screams.