简体   繁体   English

将响应发送到客户端然后在Node.js中运行长时间/繁重的操作的快速方法是什么

[英]What is the fast way to send response to the client and then run long/heavy actions in nodejs

I'm trying to figure out, what is the best/fast way to send the response from expressjs and then do log or do long actions in the server, without delaying the response to the client. 我试图弄清楚,从expressjs发送响应,然后在服务器中进行日志或执行长时间操作而又不延迟对客户端的响应的最佳/ expressjs方法是什么。

I have the following code, but I see that the response to the client is send only after the loop is finished. 我有以下代码,但是我看到对客户端的响应仅在循环完成后才发送。 I though that the response will be send because I'm triggering res.send(html); 我虽然会发送响应,因为我正在触发res.send(html); and then calling longAction 然后调用longAction

function longAction () {
    for (let i = 0; i < 1000000000; i++) {}
    console.log('Finish');
}

function myfunction (req, res) {
    res.render(MYPATH, 'index.response.html'), {title: 'My Title'}, (err, html) => {
        if (err) {
            re.status(500).json({'error':'Internal Server Error. Error Rendering HTML'});
        }
        else {
            res.send(html);
            longAction();            
        }
    });
}


router.post('/getIndex', myfunction);

What is the best way to send the response and then run my long/heavy actions? 发送响应然后执行长时间/繁重的操作的最佳方法是什么? Or What I'm missing? 还是我想念的东西?

I'm trying to figure out, what is the best/fast way to send the response from expressjs and then do log or do long actions in the server, without delaying the response to the client. 我试图弄清楚,从expressjs发送响应,然后在服务器中进行日志或执行长时间操作而又不延迟对客户端的响应的最佳/最快方法是什么。

The best way to do this is to only call longAction() when express tells you that the response has been sent. 最好的方法是仅在express告诉您响应已发送时才调用longAction() Since the response object is a stream, you can use the finish event on that stream to know when all data from the stream has been flushed to the underlying OS. 由于响应对象是一个流,因此您可以在该流上使用finish事件来了解何时将流中的所有数据刷新到基础OS。

From the writable stream documentation : 可写流文档中

The 'finish' event is emitted after the stream.end() method has been called, and all data has been flushed to the underlying system. 在调用stream.end()方法并将所有数据刷新到基础系统之后,将发出“结束”事件。

Here's how you could use that in your specific code: 这是在特定代码中使用它的方式:

function myfunction (req, res) {
    res.render(MYPATH, 'index.response.html'), {title: 'My Title'}, (err, html) => {
        if (err) {
            res.status(500).json({'error':'Internal Server Error. Error Rendering HTML'});
        }
        else {
            res.on('finish', () => {
                longAction();            
            });
            res.send(html);
        }
    });
}

For a little more explanation on the finish event, you can start by looking at the Express code for res.send() and see that is ends up calling res.end() to actually send the data. 有关finish事件的更多说明,您可以先查看Express代码中的res.send()然后看看最终是调用res.end()来实际发送数据。 If you then look at the documentation for .end() on a stream writable, it says this: 如果然后在可写流上查看.end()文档 ,它会说:

Calling the writable.end() method signals that no more data will be written to the Writable. 调用writable.end()方法表示没有更多数据将被写入Writable。 The optional chunk and encoding arguments allow one final additional chunk of data to be written immediately before closing the stream. 可选的块和编码参数允许在关闭流之前立即写入最后一个额外的数据块。 If provided, the optional callback function is attached as a listener for the 'finish' event. 如果提供,则可选的回调函数将作为“ finish”事件的侦听器附加。

So, since Express doesn't expose access to the callback that .end() offers, we just listen to the finish event ourselves to be notified when the stream is done sending its last bit of data. 因此,由于Express不会公开对.end()提供的回调的访问,因此,我们只是自己侦听finish事件,以在流完成发送其最后一位数据时得到通知。


Note, there is also a typo in your code where re.status(500) should be res.status(500) . 请注意,您的代码中还有一个错字,其中re.status(500)应该是res.status(500)

Use setImmediate : 使用setImmediate:

var test = function(){
    for (let i = 0; i < 1000000000; i++) {}
    console.log('Finish');
}

router.get("/a", function(req, res, next){
    setImmediate(test);
    return res.status(200).json({});
});

Your long function will be executed at the end of the current event loop cycle. 您的long函数将在当前事件循环周期的结尾执行。 This code will execute after any I/O operations (in this sample, first I/O operation is the res.status(200)) in the current event loop and before any timers scheduled for the next event loop. 该代码将在当前事件循环中的任何I / O操作(在此示例中,第一个I / O操作为res.status(200))之后,在为下一个事件循环安排的任何计时器之前执行。

Thanks for the answers: 感谢您的回答:

After checking I think the best approach is using listening to finish event 经过检查,我认为最好的方法是使用监听finish事件

res.on('finish', () => {
    // Do another stuff after finish got the response
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM