Question

I am calling a google api function repeatedly in a loop. Each time i want to remove one element of the address array.

for(var i = 0; i < AddressObject.addressToArray.length; i++ ){              
  srPerformGeocode(AddressObject);    
  console.log(AddressObject.addressToArray);
  AddressObject.addressToArray.splice(0, 1);                
}
    // --------------------------------------------------------------
    //  Perform geocoding 
    // --------------------------------------------------------------
    function srPerformGeocode(AddressObject)
    {       
        address = AddressObject.addressToArray.join(",");   
        console.log(AddressObject.addressToArray);      
        if (geocoder){                      
            geocoder.geocode({'address': address.trim() }, function (results, status) 
            {                                           
                if (status == google.maps.GeocoderStatus.OK){                   
                    console.log("geocoded " + AddressObject.addressToArray);
                                        // Do something 
                }
                else{                   
                    alert("FAIL");
                }
            }); 
        }
    }

What appears to be happening is that the loop is running i times, and function srPerformGeocode is running i times each time using the last value of the i loop.

['field1' ,'field2' ,'field3' ,'field4' ]
['field1' ,'field2' ,'field3' ]
['field1' ,'field2'  ]
['field1'  ]
geocoded field1
geocoded field1
geocoded field1
geocoded field1
Was it helpful?

Solution

This isn't a closure issue, because you never actually close over any variables (other than address in srPerformGeocode, but that's inconsequential to your problem). Read more about what closures are here.

JavaScript passes objects by reference1, so you're passing the same array around on each iteration of the loop (and thus, each invocation of srPerformGeocode).

This is actually fine until the asynchronous callback is executed – the geocoding service is called with the parameters you see in the first set of console outputs, but when the callback is executed and you log "geocoded ...", you'll only see AddressObject as it existed after the last iteration of the loop, because the loop finished a long time ago (this is very nature of async JavaScript).

The way to fix your problem is to pass each invocation of srPerformGeocode a copy of the AddressObject array. Conveniently, arrays have a method that return a new array – slice. (splice only modifies an array; it does not create a new one.)

for (var i = 0; i < AddressObject.addressToArray.length; i++) {              
 srPerformGeocode(AddressObject.slice(0, -i));
}

(Note that this is a shallow copy; that is, any objects in the array still point to the same objects as the original array. This doesn't matter here since your array is full of primitives.)

Some other notes:

  1. String.trim doesn't exist in IE ≤ 8, so your call to address.trim() will throw an exception.
  2. Google Maps doesn't really do bulk geocoding; you may find yourself getting rate limited or even shut down completely if you make too many geocoding calls.

1 Pedants will disagree with this statement; see the linked answer.

OTHER TIPS

At the time your callback is invoked by the Geocoding API, AddressObject.addressToArray has been changed. You'd be better to hold on to the string you passed (rather than the object).

I suspect this will give you the results you're after:

console.log("geocoded " + address.trim());
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top