0

I have below a simple block of JavaScript (stripped down) from a project I'm working on that uses Node.js and Request.

In the third instance where I'm writing to the console, I'm failing to see why the variable xxx is undefined. I understand it's a scope issue, but I thought that nested functions like this had access to variables in their parent scope.

I'm certain I'm misunderstanding scope as it pertains to this scenario. Can someone explain to me what is preventing access to this variable, and perhaps a suggestion on how I can otherwise pass this variable into this function to use?

Update - verbatim code below:

In the case below, groups is defined at the top and is a root-level variable.

(function getListings() {
  for (var i = 0; i < groups.length; i++) {
    console.log("1: " + JSON.stringify(groups[i]));
    var listingReqOptions = { url: format(listingsEndpoint, { "groupID": groups[i][1], "listingID": groups[i][0] }) };
    console.log("2: " + JSON.stringify(groups[i]));
    request(listingReqOptions, function (error, response, body) {
      console.log("3: " + JSON.stringify(groups[i]));
      if (!error && response.statusCode == 200) {
        var channel = format(listingsChannelTemplate, { "groupID": groups[i][1], "listingID": groups[i][0] });
        console.log("adding channel: " + channel);
        listingChannels[channel] = JSON.parse(body);
      }
      else {
        console.log("An error occurred while retrieving listing data: " + JSON.stringify(response));
      }
    });
  }
  // Loop
  setTimeout(getListings, 3000);
})();
trnelson
  • 2,715
  • 2
  • 24
  • 40
  • 2
    It seems you've probably stripped down the example too far. The snippet you posted shouldn't have the problem you describe. Look for any other uses of `xxx` beyond this. Is it assigned or declared anywhere else? – Jonathan Lonowski Oct 31 '14 at 19:25
  • Sorry gents, let me re-post my actual code. I tried to strip it down so it wouldn't be a nightmare to read. :) One moment. – trnelson Oct 31 '14 at 19:26
  • Code updated, please ignore some of the repetition. Needed to get it working before I refactor out the junk. – trnelson Oct 31 '14 at 19:29
  • 1
    try to use bind if you must use immediate invocation ,(function getListings() { / your code }).bind(xxx)(); – guramidev Oct 31 '14 at 19:30
  • 2
    For the revised snippet, take a look at "[JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example)." The issue that `request()` is asynchronous, so it completes after the `for` loop has already continued to increment `i`, at a time when `groups[i]` is equivalent to `groups[groups.length]`. – Jonathan Lonowski Oct 31 '14 at 19:30
  • 2
    Looks like a classic case of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/q/750486/710446) -- by the time any `request` call resolves, the loop has finished and `i` is out of bounds – apsillers Oct 31 '14 at 19:30
  • 1
    I think this duplicate should answer your question. If you feel that the duplicate doesn't help you resolve your problem, please edit your question to highlight what you don't understand about the answers in the duplicate, and I'd be happy to reopen. – apsillers Oct 31 '14 at 19:35
  • Ahhh, that definitely makes sense. I understand what's happening here now. For some reason I assumed that each case of `request()` would sort of happen within a scope that still contained `i` at the value it was when it was called. – trnelson Oct 31 '14 at 19:35
  • @apsillers No worries, it's certainly a duplicate issue. I looked before posting, but couldn't find something that explained the situation, but I wasn't specifically searching for a scenario that contained a loop. Thanks! – trnelson Oct 31 '14 at 19:36

1 Answers1

1

try this, in case of context switch this is safer:

var xxx = "HEY LOOK AT ME";
console.log(xxx); // <-- "HEY LOOK AT ME"
var opts = { url: "http://example.com/foobaz" };
console.log(xxx); // <-- "HEY LOOK AT ME"

request(opts, function(e) { return function (error, response, body) {
  console.log(e);
  if (!error && response.statusCode == 200) {
    // stuff happens here
  }
}}(xxx));
Sebas
  • 21,192
  • 9
  • 55
  • 109
  • Oh, interesting. So that's how I would pass a variable into that function. Thank you for the info! If all else fails, I will definitely try that approach. – trnelson Oct 31 '14 at 19:30