0

What does that even mean in terms of the following code, the res.send works fine however in my console i get the following message:

http.js:689
throw new Error('Can\'t set headers after they are sent.');

app.get('/summoner/:summonerName', function(req, res) {
lolapi.Summoner.getByName(req.params.summonerName, function(err, obj) {
  var options = {
    beginIndex: 0,
    endIndex: 1
  };
  lolapi.MatchList.getBySummonerId(obj['savisaar2'].id, options, function(err, matches) {
    var gameMatches = matches.matches;
    for(var i = 0; i < gameMatches.length; i++) {
      lolapi.Match.get(gameMatches[i].matchId, function(err, games) {
        res.send({profile : obj, matchHistory : games});
      });
    }
  });
});
});
  • Possible duplicate of [Node.js Error: Can't set headers after they are sent](http://stackoverflow.com/questions/7042340/node-js-error-cant-set-headers-after-they-are-sent) – omarjmh Apr 07 '16 at 01:43
  • You can only call `res.send()` once per request. You are calling it multiple times in a loop. – jfriend00 Apr 07 '16 at 01:45
  • What is the intent of this code? You are calling `lolapi.Match.get()` inside a `for` loop and then attempting to do `res.send()` for each `lolapi.Match.get()`. Calling `res.send()` more than once for a given request is what causes the error you see. But, how is the code supposed to work? What are you supposed to do with all your results? Are you trying to collect all the results together and then send them all as a response? – jfriend00 Apr 07 '16 at 02:22

1 Answers1

0

As I explained in my comments, you are calling res.send() inside a for loop which means you are calling it more than once. You can only call it once per request. That is why you are seeing the error message in the console.

It is not clear exactly what your code really wants to do, but if the desire is to collect all the results into an array and send them all as the response, then you can do that like this:

app.get('/summoner/:summonerName', function (req, res) {
    lolapi.Summoner.getByName(req.params.summonerName, function (err, obj) {
        if (err) {
            return res.status(500).end();
        }
        var options = {beginIndex: 0, endIndex: 1};
        lolapi.MatchList.getBySummonerId(obj['savisaar2'].id, options, function (err, matches) {
            var gameMatches = matches.matches;
            var results = [];
            for (var i = 0; i < gameMatches.length; i++) {
                lolapi.Match.get(gameMatches[i].matchId, function (err, games) {
                    if (err) {
                        return res.status(500).end();
                    }
                    results.push({profile: obj, matchHistory: games});
                    // if all results are done, then send response
                    if (results.length === gameMatches.length) {
                        res.json(results);
                    }
                });
            }
        });
    });
});

Note: I've also added rudimentary error handling.

If you want the results in the particular order that you requested then, then you can add a little more code to do that like this:

app.get('/summoner/:summonerName', function (req, res) {
    lolapi.Summoner.getByName(req.params.summonerName, function (err, obj) {
        if (err) {
            return res.status(500).end();
        }
        var options = {beginIndex: 0, endIndex: 1};
        lolapi.MatchList.getBySummonerId(obj['savisaar2'].id, options, function (err, matches) {
            var gameMatches = matches.matches;
            var results = new Array(gameMatches.length);
            var cntr = 0;
            for (var i = 0; i < gameMatches.length; i++) {
                (function(index) {
                    lolapi.Match.get(gameMatches[i].matchId, function (err, games) {
                        if (err) {
                            return res.status(500).end();
                        }
                        ++cntr;
                        results[index] = {profile: obj, matchHistory: games};
                        // if all results are done, then send response
                        if (cntr === gameMatches.length) {
                            res.json(results);
                        }
                    });
                })(i);
            }
        });
    });
});

Since Promises are now standard in 2016, here's an idea what this could look like using the Bluebird promise library:

const Promise = require('bluebird');
Promise.promisifyAll(lolapi.Summoner);
Promise.promisifyAll(lolapi.MatchList);
Promise.promisifyAll(lolapi.Match);

app.get('/summoner/:summonerName', function (req, res) {
    var main;
    lolapi.Summoner.getByNameAsync(req.params.summonerName).then(function(obj) {
        main = obj;
        var options = {beginIndex: 0, endIndex: 1};
        return lolapi.MatchList.getBySummonerIdAsync(obj['savisaar2'].id, options);
    }).then(function(matches) {
        var gameMatches = matches.matches;
        return Promise.map(gameMatches, function(item){
            return lolapi.Match.getAsync(item.matchId).then(function(games) {
                return {profile: main, matchHistory: games};
            });
        });
    }).then(function(results) {
        res.json(results);
    }).catch(function(err) {
        res.status(500).end();
    });
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979