0

I have a global array and in a function I make a call to the geocoder api. The problem is that the array is not visible in the result function. I get the error message:

Uncaught TypeError: Cannot set property 'address' of undefined

Here is the code:

var locationsArray = new Array();

function GetAddress(){
  for(var i=0;i<locationsArray.length;i++)
    $.getJSON("https://maps.googleapis.com/maps/api/geocode/json?latlng="+locationsArray[i].position.lat()+','+locationsArray[i].position.lng(), function (result) {
      locationsArray[i].address = result.results[0].formatted_address;
    });
  console.log(locationsArray);
}

Have any idea why? Thanks.

Moraru Alex
  • 149
  • 1
  • 1
  • 6
  • possible duplicate of [How to return the response from an Ajax call?](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-ajax-call) – Scimonster Nov 18 '14 at 18:45
  • @Scimonster: In this case, I don't think so. It appears the OPs error is coming inside the callback and it's not from trying to access the returned value. – Matt Burland Nov 18 '14 at 18:47
  • @MattBurland Relooking the code... i retracted the CV. But perhaps the OP stripped some of the code that populates `locationArray`? – Scimonster Nov 18 '14 at 18:49
  • Fixed it, thanks Scimonster. – Moraru Alex Nov 18 '14 at 19:01
  • Umm ... wait. Why does it matter whether `locationsArray` is initially populated? This function, properly implemented, shouldn't throw errors either way ... – svidgen Nov 18 '14 at 19:07
  • Found a duplicate: http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example Unfortunately, now i can't close it... (@MattBurland) – Scimonster Nov 18 '14 at 19:21
  • 1
    @Scimonster Your 2nd link is definitely on-target. Voting to close ... – svidgen Nov 18 '14 at 19:24
  • @MattBurland I submitted my answer before I saw the link and submitted my VTC. – svidgen Nov 18 '14 at 20:10
  • Also, I don't see a problem with posting an answer for "consumption" while the close is pending ... if ever it's fully confirmed. – svidgen Nov 18 '14 at 20:11

1 Answers1

1

TL;DR

Why aren't you using .each()? You seem to be using jQuery anyway ...

function GetAddress() {
  $.each(loationsArray, function(i, v) {
    $.getJSON("https://maps.googleapis.com/maps/api/geocode/json?latlng="
               + v.position.lat() + ',' + v.position.lng(),
       function (result) {
          v.address = result.results[0].formatted_address;
       }
    );
    console.log(v);
  });
}

... admittedly untested. But, if I've "transliterated" it correctly, your v should be properly scoped in that implementation.


It's not that your callback can't see locationsArray. If that were the case, your error would complain about locationsArray or property 0 of locationsArray not being defined.

The real trouble is, your callback refers to the i from the for-loop's scope. After the last iteration, it actually increments i to be locationsArray.length. The loop-condition is tested, fails, and the loop is exited. But, i is still locationsArray.length -- a value that never existed. (Though, in a related problem, the collection could also be modified before the callback fires.)

Let's take an example:

var arr = [1,2,3];
for (var i = 0; i < arr.length; i++) {
  setTimeout(function() {
    console.log(i, arr, arr[i]);
  }, i * 100);
}

The Chrome console will show the following:

> 3 [1, 2, 3] undefined
> 3 [1, 2, 3] undefined
> 3 [1, 2, 3] undefined

To fix our example, we would "scope out" a reference to the particular item and put that in our callback. Something like this:

var arr = [1,2,3];
for (var i = 0; i < arr.length; i++) {
  // create a scope
  (function() {
    var v = arr[i];
    setTimeout(function() {
      console.log(v);
    }, i * 100);
  })();
}

... And the console shows:

> 1
> 2
> 3
svidgen
  • 13,744
  • 4
  • 33
  • 58