异步发送错误后无法设置标头

Posted

技术标签:

【中文标题】异步发送错误后无法设置标头【英文标题】:Can't set headers after they are sent error with async 【发布时间】:2015-07-20 21:42:31 【问题描述】:

我在下面的代码中尝试了一个相当复杂的计算。我正在尝试使用 api https://api.github.com/repos/marklogic/java-client-api/issues?page=1&per_page=10 从给定项目中的 github 获取错误列表。从错误列表中,我试图从它们对应的端点获取每个问题对应的eventscommentshttps://api.github.com/repos/marklogic/java-client-api/issues/291/eventshttps://api.github.com/repos/marklogic/java-client-api/issues/291/comments。 我正在使用async 库。我正在使用waterfall 函数和parallel 函数为每个错误返回一个合并的JSON,这样每个问题都会在每个问题的相同响应中包含评论和事件。问题是它抛出 Can't set headers after they are sent 错误并指向第 2 行,我理解错误在说什么,但我不知道如何解决它,因为注释掉任何一个有问题的行都会导致请求挂起,因为服务器没有发送响应。请帮忙!在此先感谢

exports.listGitHubBugs = function(req, res) 
    var _page = req.query.page || 1;
    var _per_page = req.query.per_page || 25;
    var finalResult = []
        //console.log('url:', 'https://api.github.com/repos/marklogic/' + req.query.project + '/issues?page=' + _page + '&per_page=' + _per_page);
    var options = 
        url: 'https://api.github.com/repos/marklogic/' + req.query.project + '/issues?page=' + _page + '&per_page=' + _per_page,
        headers: 
            'User-Agent': req.query.project
        ,
        auth: githubAuth
    ;
    request(options, function(error, response, body) 
        if (error) 
            console.log(error);
            res.send(error);
        

        if (!error && response.statusCode === 200) 
            var issues = JSON.parse(response.body)

            async.waterfall([
                 // get comments & events for all bugs and then send the response
                function(callback) 
                    issues.forEach(function(issue) 
                          // for each bug, get comments and events
                        async.parallel([

                            function(parallelCallback) 
                                var options = 
                                    url: issue.events_url,
                                    headers: 
                                        'User-Agent': getProjectNameFromURL(issue.events_url)
                                    ,
                                    auth: githubAuth
                                ;
                                request(options, function(error, response, body) 
                                    if (error) 
                                        console.log('ERROR', error);
                                        parallelCallback(error)
                                    
                                    if (!error && response.statusCode === 200) 
                                        // console.log('events:', body);
                                        parallelCallback(null, body)

                                    


                                )
                            ,
                            function(parallelCallback) 
                                var options = 
                                    url: issue.comments_url,
                                    headers: 
                                        'User-Agent': getProjectNameFromURL(issue.comments_url)
                                    ,
                                    auth: githubAuth
                                ;
                                request(options, function(error, response, body) 
                                    if (error) 
                                        console.log('ERROR', error);
                                        parallelCallback(error)
                                    
                                    if (!error && response.statusCode === 200) 
                                        //  console.log('comments:', body);
                                        parallelCallback(null, body)
                                    


                                )
                            
                        ], function(err, result) 
                            if (err) 
                                console.log('ERROR:', err);
                                callback(err);
                            
                            console.log('parallel process done');
                            issue.events = JSON.parse(result[0]);
                            issue.comments = JSON.parse(result[1]);
                            finalResult.push(issue)
                            callback(null, finalResult) // offending line#1
                        )
                    ) // forEach end
                
            ], function(err, result) 
                if (err) 
                    res.send(err);
                
                console.log('waterfall done');
                console.log(result);
                res.send(result); // offending line#2
            )

         // if end
    ) // reqest end


错误

UncaughtException: Can't set headers after they are sent.
ERROR Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (http.js:691:11)
    at ServerResponse.res.set.res.header (/Users/sreddy/space/angularjs/BugTrack/node_modules/express/lib/response.js:524:10)
    at ServerResponse.res.send (/Users/sreddy/space/angularjs/BugTrack/node_modules/express/lib/response.js:125:10)
    at ServerResponse.res.json (/Users/sreddy/space/angularjs/BugTrack/node_modules/express/lib/response.js:191:15)
    at /Users/sreddy/space/angularjs/BugTrack/server/api/common/common.controller.js:163:33
    at /Users/sreddy/space/angularjs/BugTrack/server/api/common/common.controller.js:153:29
    at /Users/sreddy/space/angularjs/BugTrack/node_modules/async/lib/async.js:254:17
    at done (/Users/sreddy/space/angularjs/BugTrack/node_modules/async/lib/async.js:135:19)
    at /Users/sreddy/space/angularjs/BugTrack/node_modules/async/lib/async.js:32:16
    at /Users/sreddy/space/angularjs/BugTrack/node_modules/async/lib/async.js:251:21

最终工作代码

exports.listGitHubBugs = function(req, res) 
var _page = req.query.page || 1;
var _per_page = req.query.per_page || 25;
var finalResult = []
    //console.log('url:', 'https://api.github.com/repos/marklogic/' + req.query.project + '/issues?page=' + _page + '&per_page=' + _per_page);
var options = 
    url: 'https://api.github.com/repos/marklogic/' + req.query.project + '/issues?page=' + _page + '&per_page=' + _per_page,
    headers: 
        'User-Agent': req.query.project
    ,
    auth: githubAuth
;
request(options, function(error, response, body) 
    if (error) 
        console.log(error);
        return res.send(error);
    

    if (!error && response.statusCode === 200) 
        var issues = JSON.parse(response.body)

        async.waterfall([
            // get events and comments for all bugs and return the final processes list of bugs
            function getEventsAndCommentsForAllBugs(callback) 
                issues.forEach(function getEventsAndComments(issue, index) 
                    // for each bug, get comments and events
                    async.parallel([

                        function getEvents(parallelCallback) 
                            var options = 
                                url: issue.events_url,
                                headers: 
                                    'User-Agent': getProjectNameFromURL(issue.events_url)
                                ,
                                auth: githubAuth
                            ;
                            request(options, function(error, response, body) 
                                if (error) 
                                    console.log('ERROR', error);
                                    parallelCallback(error)
                                
                                if (response.statusCode === 200) 
                                    // console.log('events:', body);
                                    parallelCallback(null, body)

                                


                            )
                        ,
                        function getComments(parallelCallback) 
                            var options = 
                                url: issue.comments_url,
                                headers: 
                                    'User-Agent': getProjectNameFromURL(issue.comments_url)
                                ,
                                auth: githubAuth
                            ;
                            request(options, function(error, response, body) 
                                if (error) 
                                    console.log('ERROR', error);
                                    parallelCallback(error)
                                
                                if (response.statusCode === 200) 
                                    //  console.log('comments:', body);
                                    parallelCallback(null, body)
                                


                            )
                        
                    ], function attachEventsAndComments(err, result) 
                        if (err) 
                            console.log('ERROR:', err);
                            callback(err);
                        
                        console.log('parallel process done');
                        issue.eventList = JSON.parse(result[0]);
                        issue.commentList= JSON.parse(result[1]);
                        finalResult.push(issue)
                        if (index === (issues.length - 1)) 
                            callback(null, finalResult)
                        
                        //
                    )
                ) // forEach end

            
        ], function processedBugs(err, result) 
            if (err) 
                res.send(err);
            
            console.log('waterfall done');
            console.log(result);
            res.send(result);
        )

     // if end
) // reqest end

【问题讨论】:

相关信息:Node.js Error: Can't set headers after they are sent. 【参考方案1】:

您能否提供代码的完整工作示例,我们可以尝试一下。

这就是说,这个源代码有几个错误。

在第一次请求时,如果发生错误,您将其写入 app.response,但您不会停止执行。因此,如果发生错误,您将写入两次响应对象。

你应该这样做 if (error) console.log(error); return res.send(error);

而不是 if (error) console.log(error); res.send(error);

然后,这可以改变 if (!error && response.statusCode === 200) if (response.statusCode === 200)

在获取问题事件和cmets时出现同样的错误,请考虑修复它。

还有在async的最后回调中。//

并且在 async.waterfall 的最终回调中。

最后,我建议您使用命名函数。这将通过提供更有意义的错误堆栈跟踪来帮助您进行调试。

例如,而不是做, async.prallel([function()/* code here*/]);

你会写 async.parallel([function nameOfTheTask()/* code here*/]);

还可以考虑使用 linter,例如 eslint,缺少几个 ; 可能会破坏您的代码,请参阅 http://eslint.org/

【讨论】:

非常感谢您的宝贵反馈。我已经实施了您的建议并发布了最终的工作代码【参考方案2】:

我想通了。我为每次迭代调用 parellelCallback() 而不是在循环结束时调用它。如果只需要条件就很简单。

if (index === (issues.length-1)) 
    callback(null, finalResult)
  

【讨论】:

以上是关于异步发送错误后无法设置标头的主要内容,如果未能解决你的问题,请参考以下文章

错误:发送后无法设置标头。

为啥显示此错误?发送到客户端后无法设置标头

不断收到错误:发送后无法设置标头

UnhandledPromiseRejectionWarning:错误:发送后无法设置标头[重复]

仅在 Cloud9 上出现错误“发送后无法设置标头”

解析服务器(快速)错误:发送后无法设置标头