Skip to content
Tech News
← Back to articles

Node.js worker threads are problematic, but they work great for us

read original get Node.js Worker Threads Book → more articles
Why This Matters

This article highlights the challenges and solutions associated with using worker threads in Node.js, especially in high-pressure scenarios where CPU-intensive tasks can block the event loop. Moving critical processes to worker threads improves reliability and performance, demonstrating the importance of multi-threading in modern JavaScript environments for both developers and consumers. It underscores the need for careful thread management to ensure scalable, responsive applications in the tech industry.

Key Takeaways

Worker threads solve real problems, but they come with constraints that Go, Rust, and Python developers would never expect. Here's what we learned moving Inngest Connect's internals off the main thread.

Node.js runs on a single thread. That's usually fine. The event loop handles I/O concurrency without you thinking about locks, races, or deadlocks. But "single-threaded" has a cost that only shows up under pressure: if your JavaScript monopolizes the CPU, nothing else runs. No timers fire. No network callbacks execute. No I/O completes.

We ran into this with Inngest Connect, a persistent WebSocket connection between your app and the Inngest server. Connect is an alternative to our HTTP-based model (our serve function) that reduces TCP handshake overhead and avoids long-lived HTTP requests during long-running steps. Workers send heartbeats over the WebSocket so the server knows they're alive.

The problem: Users reported "no available worker" errors despite their workers running.

Users reported "no available worker" errors despite their workers running. The cause: CPU-heavy user code was monopolizing the main thread, starving the event loop, and blocking heartbeats. The server assumed the workers were dead and stopped routing work to them.

CPU-heavy user code was monopolizing the main thread, starving the event loop, and blocking heartbeats. The server assumed the workers were dead and stopped routing work to them. The fix: Move Connect internals into a worker thread.

But getting there taught us a few things about how worker threads actually work in Node.js, and how they compare to threading models in other languages.

Connect's worker thread isolation is a new feature in v4 of the Inngest TypeScript SDK. Upgrade to get it automatically. This post focuses on Node.js, but Bun and Deno also support worker threads.

Event loop starvation

Node's event loop processes callbacks in phases: timers, I/O polling, setImmediate , close callbacks. Between each phase, it checks for microtasks (resolved promises, queueMicrotask ). The critical property: the loop can only advance when the current JavaScript execution yields.

... continue reading