No more limits for CPU loads
Node.js 10.5 introduced worker threads and microjob is a tiny wrapper around them.
microjob helps you to spawn new threads with ease, just like calling an anonymous function, interfaced with promises and async/await mechanism.
A brief history of Node.js
Node.js is famous for its event loop, a powerful mechanism for handling I/O bound operations with high performance.
The early implementations of the asynchronous interface were carried out with callbacks, then they evolved into promises and finally into the well known async/await pattern.
Today, using Node.js for I/O loads is common, like HTTP requests, DB queries, file system calls, timers, tickers and so on, and of course Node.js is really powerful for these operations.
However, Node.js has always suffered from a lack of a good system to sustain heavy CPU loads.
CPU bound operations
But what are those “heavy CPU loads”?
Here’s a good explanation from a Quora answer:
- Search algorithms
- Video/Audio/Content conversion/compression algorithms
- Video streaming
- Graphics processing/rendering e.g games, videos
- Heavy mathematical computations like matrix multiplication, calculating factorials, finding prime numbers etc.
For these operations, Node.js has always suffered because of its nature: non-blocking single-threaded event-loop.
The main feature of Node.js is the event-loop, a mechanism where threads are spawned and handled underneath. However, this works only with asynchronous operations but the sync ones are executed on a single thread and there’s just one thread in execution at a time.
Meaning that if a long-running job gets executed, the main thread remains blocked until the execution is finished, blocking the whole system.
To prevent such inconvenience, Node.js supports multiprocessing but the context-switch between processes is expensive.
With version 10.5, Node.js shipped worker threads, multithreading in all of its splendor!
You can just spawn a new thread and let it handle those heavy CPU loads.
So, how does it work?
Worker threads come with the event interface and they can communicate with the main thread via message passing.
The example inside the docs is really self explaining.
Working with events and message passing is good with long-living jobs, where you need to have a running thread in background but what if I want to run just a one-shot function, carrying a heavy CPU load, in a separated thread?
In languages, such as GoLang, you can make it effortlessly with lightweight routines:
That fmt.Println is executed in a different thread with an anonymous function defined and invoked inline.
What if you can make it in the same way with Node.js’s worker threads?
And that’s why I’ve created microjob:
A tiny wrapper that allows you to work with async/await interface, skipping the boring part of instantiating a new thread and sync with it through events.
What’s under the hood?
microjob starts a new thread and sends the given function to it with the desired data, executing (evaluating) it in the specified context.
Meaning that you can do this magic pretty like you’d do in GoLang:
And you can actually pass custom data:
What about having async functions invoked inside the worker?
The future of microjob
microjob is evolving rapidly, due to users’ requests and the constant changing ecosystem of Node.js.
It’s having a good feedback from the community, earning something like 800+ stars on Github in the first week, scaling the Github Trending page.
Soon, microjob will have its own thread pool system, then it will be rewritten in Typescript and it will support CI/CD with unit testing.
Finally, I’m glad JS people are so interested in having multithreading in Node.js!