I am trying to get my head around creating a non-blocking piece of heavy computation in nodejs. Take this example (stripped out of other stuff):
http.createS
This is a classic misunderstanding of how the event loop is working.
This isn't something that is unique to node - if you have a long running computation in a browser, it will also block. The way to do this is to break the computation up into small chunks that yield execution to the event loop, allowing the JS environment to interleave with other competing calls, but there is only ever one thing happening at one time.
The setImmediate
demo may be instructive, which you can find here.
Although this is an old post(8 years ago), try to add some new updates to it.
For Nodejs application to get good performance, the first priority is never blocking the event loop. The sleep(10000)
method breaks this rule. This is also the reason why Node.js is not suitable for the CPU intensive application. Since the big CPU computation occurs on the event loop thread(it's also the main and single thread of node.js)and will block it.
Multithread programming work_threads was introduced into node.js ecosystem since version 12. Compared with multi-process programming, it's lightweight and has less overhead.
Although multithread was introduced into node.js, but Node.js is still based on the event driven model and async non-block IO. That's node.js's DNA.
You can't do that directly, without using some of the IO modules in node (such as fs
or net
). If you need to do a long-running computation, I suggest you do that in a child process (e.g. child_process.fork
) or with a queue.
We (Microsoft) just released napajs that can work with Node.js to enable multithreading JavaScript scenarios in the same process.
your code will then look like:
var napa = require('napajs');
// One-time setup.
// You can change number of workers per your requirement.
var zone = napa.zone.create('request-worker-pool', { workers: 4 });
http.createServer(function(req, res) {
console.log(req.url);
zone.execute((request) => {
var result = null;
// Do heavy computation to get result from request
// ...
return result;
}, [req]).then((result) => {
res.end(result.value);
}
}).listen(8080, function() { console.log("ready"); });
You can read this post for more details.
If you computation can be split into chunks, you could schedule executor to poll for data every N seconds then after M seconds run again. Or spawn dedicated child for that task alone, so that the main thread wouldn't block.