- JavaScript is single-threaded, yes. That's a feature.
- node.js is aggressively async, so network or disk reads do not block. As a consequence, node.js is very fast, because it avoids both blocking and CPU context-changes.
- WorkerThreads allow multi-threading. But these run in a separate JS context (sandbox), which doesn't share variables with the main thread, so the usual memory access problems of multi-threaded programming don't apply. Data is exchanged between main and WorkerThread only through defined APIs. This means that starting a WorkerThread is expensive, and data exchange with the main thread is also expensive, so they are used only when absolutely necessary. One use case is CPU-expensive calculations.
From the node.js documentation: "Workers (threads) are useful for performing CPU-intensive JavaScript operations. They do not help much with I/O-intensive work. The Node.js built-in asynchronous I/O operations are more efficient than Workers can be."