向客户端发送响应然后在nodejs中运行长/重动作的快速方法是什么?

时间:2018-04-27 14:50:26

标签: javascript node.js express

我试图找出,从expressjs发送响应然后在服务器中记录或执行长操作的最佳/快速方式是什么,而不会延迟对客户端的响应。

我有以下代码,但我发现只有在循环完成后才会发送对客户端的响应。我虽然会发送回复,因为我正在触发res.send(html);然后调用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);

发送响应然后执行长/重动作的最佳方法是什么? 或者我缺少什么?

3 个答案:

答案 0 :(得分:4)

  

我试图找出,从expressjs发送响应然后在服务器中记录或执行长时间操作的最佳/快速方式是什么,而不会延迟对客户端的响应。

执行此操作的最佳方法是,当express表示已发送响应时,仅调用longAction()。由于响应对象是流,因此您可以使用该流上的finish事件来了解流中的所有数据何时刷新到底层操作系统。

来自writable stream documentation

  

调用stream.end()方法后会发出'finish'事件,并且所有数据都已刷新到底层系统。

以下是您在特定代码中使用它的方法:

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);
        }
    });
}

有关finish事件的更多解释,您可以先查看Express code for res.send(),然后看到最终调用res.end()来实际发送数据。如果您然后查看可写流上的documentation for .end(),它会说:

  

调用writable.end()方法表示不再将数据写入Writable。可选的块和编码参数允许在关闭流之前立即写入最后一个额外的数据块。如果提供,则附加可选回调函数作为'finish'事件的监听器。

因此,由于Express不公开对.end()提供的回调的访问,我们只是自己收听finish事件,以便在流完成发送其最后一位数据时得到通知。

注意,您的代码中还有一个拼写错误re.status(500)应为res.status(500)

答案 1 :(得分:0)

使用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({});
});

您的长函数将在当前事件循环周期结束时执行。此代码将在当前事件循环中的任何I / O操作(在此示例中,第一个I / O操作是res.status(200))之后以及在为下一个事件循环计划的任何计时器之前执行。

答案 2 :(得分:0)

感谢您的回答:

检查后我认为最好的方法是使用finish事件

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