Node.js / express:立即响应客户端请求并在nextTick中继续执行任务

时间:2014-01-22 16:26:47

标签: javascript node.js asynchronous express

我想将服务器耗费高的CPU任务与用户体验区分开来:

./ 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'));
});

./资源/ 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!");
  });
};

当请求“localhost:3000 / resources / test / async”时,我希望浏览器呈现“Hello world,这应该立即发送”真的很快,node.js继续处理,并且在控制台出现一段时间后“完成了“消息。

相反,浏览器会一直等待,直到node.js完成大任务,然后呈现内容。我尝试过使用res.set({ 'Connection': 'close' });res.end();,但没有按预期工作。我还用Google搜索没有运气。

如何立即将响应发送到客户端,服务器继续执行任务?

修改

在解决方案中发布了fork方法

3 个答案:

答案 0 :(得分:6)

尝试等待而不是占用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是单线程的。如果用繁忙的循环锁定CPU,整个过程就会停止,直到完成为止。

答案 1 :(得分:5)

Thakns为Peter Lyons提供帮助,最后主要的问题是firefox缓冲区:响应时间不长,以便冲洗它(所以firefox一直在等待)。

无论如何,对于高CPU执行任务,节点将一直挂起直到完成,因此不会出现新的请求。如果有人需要它,可以通过分叉来实现(使用child_process,参见http://nodejs.org/api/child_process.html中的示例)

不得不说通过分叉来改变上下文可能比在不同的时间段中分割任务需要更长的时间。

./资源/ 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' });
};

./资源/ child.js:

process.on('message', function(m) {
  console.log('CHILD got message:', m);
});

答案 2 :(得分:1)

一个好的解决方案是使用child_process.fork():它允许您在不同的Node实例中执行应用程序的另一个JavaScript文件,因此在不同的事件循环中。当然,您仍然可以通过发送消息在两个进程之间进行通信:因此,通过 UI进程,您可以向分叉进程发送消息,要求它执行东西。

例如,在ui.js

var ChildProcess = require('child_process');
var heavyTaskWorker = ChildProcess.fork('./heavyTaskWorker.js');
...
var message = {
    operation: "longOperation1",
    parameters: {
        param1: "value1",
        ...
    }
};
heavyTaskWorker.send(message);

heavyTaskWorker.js

process.on('message', function (message) {
    switch (message.operation) {
    case 'longOperation1':
        longOperation1.apply(null, message.parameters);
        break;
    ...
    }
});

在这里测试过,它运行正常!

希望有所帮助!