I would like to separate server high consuming CPU task from user experience:
./main.js:
var express = require('express');
var Test = require('./resources/test');
var http = require('http');
var main = express();
main.set('port', process.env.PORT || 3000);
main.set('views', __dirname + '/views');
main.use(express.logger('dev'));
main.use(express.bodyParser());
main.use(main.router);
main.get('/resources/test/async', Test.testAsync);
main.configure('development', function() {
main.use(express.errorHandler());
});
http.createServer(main).listen(main.get('port'), function(){
console.log('Express server app listening on port ' + main.get('port'));
});
./resources/test.js:
function Test() {}
module.exports = Test;
Test.testAsync = function(req, res) {
res.send(200, "Hello world, this should be sent inmediately");
process.nextTick(function() {
console.log("Simulating large task");
for (var j = 0; j < 1000000000; j++) {
// Simulate large loop
}
console.log("phhhew!! Finished!");
});
};
When requesting "localhost:3000/resources/test/async" I would expect the browser rendering "Hello world, this should be sent inmediately" really fast and node.js to continue processing, and after a while in console appearing "finished" message.
Instead, browser keeps waiting until node.js finishes large task and then renders the content. I've tried with res.set({ 'Connection': 'close' });
and also res.end();
but nothing works as expected. I've also googled with no luck.
How should it be to send the response to client immediately and server continue with tasks?
EDIT
posted fork method in solution
I would like to separate server high consuming CPU task from user experience:
./main.js:
var express = require('express');
var Test = require('./resources/test');
var http = require('http');
var main = express();
main.set('port', process.env.PORT || 3000);
main.set('views', __dirname + '/views');
main.use(express.logger('dev'));
main.use(express.bodyParser());
main.use(main.router);
main.get('/resources/test/async', Test.testAsync);
main.configure('development', function() {
main.use(express.errorHandler());
});
http.createServer(main).listen(main.get('port'), function(){
console.log('Express server app listening on port ' + main.get('port'));
});
./resources/test.js:
function Test() {}
module.exports = Test;
Test.testAsync = function(req, res) {
res.send(200, "Hello world, this should be sent inmediately");
process.nextTick(function() {
console.log("Simulating large task");
for (var j = 0; j < 1000000000; j++) {
// Simulate large loop
}
console.log("phhhew!! Finished!");
});
};
When requesting "localhost:3000/resources/test/async" I would expect the browser rendering "Hello world, this should be sent inmediately" really fast and node.js to continue processing, and after a while in console appearing "finished" message.
Instead, browser keeps waiting until node.js finishes large task and then renders the content. I've tried with res.set({ 'Connection': 'close' });
and also res.end();
but nothing works as expected. I've also googled with no luck.
How should it be to send the response to client immediately and server continue with tasks?
EDIT
posted fork method in solution
Share Improve this question edited Feb 4, 2018 at 13:12 Cœur 38.7k26 gold badges203 silver badges277 bronze badges asked Jan 22, 2014 at 16:26 MiquelMiquel 8,97911 gold badges63 silver badges84 bronze badges 2-
1
Did you try using
res.end
instead ofres.send
– adeneo Commented Jan 22, 2014 at 16:27 -
Yes sure, tried
res.set({ 'Connection': 'close' }); res.send(200, "Hello world, this should be sent inmediately"); res.end();
– Miquel Commented Jan 22, 2014 at 16:33
3 Answers
Reset to default 6Try waiting instead of hogging the CPU:
res.send("Hello world, this should be sent inmediately");
console.log("Response sent.");
setTimeout(function() {
console.log("After-response code running!");
}, 3000);
node.js is single-threaded. If you lock up the CPU with a busy loop, the whole thing grinds to a halt until that is done.
Thakns for Peter Lyons help, finally the main problem was firefox buffer: response was not so long as to flush it (so firefox kept waiting).
Anyway, for hight CPU performing tasks, node would keep hanged until finishing, so will not be attending new requests. If someone needs it, it can be achieved by forking (with child_process, see sample in http://nodejs/api/child_process.html)
Have to say that change of context by forking could take longer than splitting the task in different ticks.
./resources/test.js:
var child = require('child_process');
function Test() {}
module.exports = Test;
Test.testAsync = function(req, res) {
res.send(200, "Hello world, this should be sent inmediately");
var childTask = child.fork('child.js');
childTask.send({ hello: 'world' });
};
./resources/child.js:
process.on('message', function(m) {
console.log('CHILD got message:', m);
});
A good solution is to use child_process.fork()
: it allows you to execute another JavaScript file of your app in a different Node instance, and thus in a different event loop. Of course, you can still municate between the two processes by sending messages: so, from your UI process, you can send a message to the forked process to ask it to execute something.
For example, in ui.js
:
var ChildProcess = require('child_process');
var heavyTaskWorker = ChildProcess.fork('./heavyTaskWorker.js');
...
var message = {
operation: "longOperation1",
parameters: {
param1: "value1",
...
}
};
heavyTaskWorker.send(message);
And in heavyTaskWorker.js
:
process.on('message', function (message) {
switch (message.operation) {
case 'longOperation1':
longOperation1.apply(null, message.parameters);
break;
...
}
});
Tested here, and it works fine!
Hope that helps!