3

I'm trying to use jQuery to $().each through inputs, make an AJAX call based on two of the inputs and update a third input. However, the AJAX call (Google Maps reverse geocoding) has a call limit, meaning I have to limit the number of requests I make per second.

I'm trying to throttle the each by calling a setTimeout with a timeout that increases by 2 seconds for each iteration, but it just calls them all at once. Any insights on what I'm doing wrong? I'm basing my approach on this question but a few things- specifically the fact that the elements affected change with each iteration- make this a bit more complicated.

<button class="identify-locations">Identify locations</button>

<div class="row">
    <input class="address"></input>
    <input class="lat"></input>
    <input class="lng"></input>
</div>
<!-- the same thing over again 30 times -->

<script>
    $(".identify-locations").click(function(event){
        var time = 2000;
        $(".row").each(function(){
            if($(this).find(".lat").val() == '' && $(this).find(".lng").val() == ''){
                setTimeout( geocodeLocation($(this)), time);
                time += 2000;
            }
        });
    });

    function geocodeLocation(object, time){
        address = object.find(".address").val();
        var geocoder = new google.maps.Geocoder();
        geocoder.geocode({address: address},
            function(results_array, status) {
                if(status == 'OK'){
                    object.find(".lat").val( parseFloat(results_array[0].geometry.location.lat()) ); 
                    object.find(".lng").val( parseFloat(results_array[0].geometry.location.lng()) );
                    updateCount();
                }
        });
    }

</script>
Community
  • 1
  • 1
caitlin
  • 2,769
  • 4
  • 29
  • 65

3 Answers3

1

You are calling setTimeout with a function that returns the result immediately.

Instead do

if($(this).find(".lat").val() == '' && $(this).find(".lng").val() == ''){
 var $current = $(this);
 setTimeout(function() { geocodeLocation($current)}, time);
 time += 2000;
}

Also check out Passing parameters into a closure for setTimeout

Note that the useful .bind is not available in IE8/Safari5

Community
  • 1
  • 1
mplungjan
  • 169,008
  • 28
  • 173
  • 236
1

the problem is that you are calling the function geocodeLocation at:

setTimeout( geocodeLocation($(this)), time);

instead you are supposed to only point to its label. Therefore, it should be:

setTimeout( geocodeLocation.bind(null, $(this)), time);
trk
  • 2,106
  • 14
  • 20
1

Try utilizing index of .each()

 var time = 2000;
 $(".row").each(function(index){
   if($(this).find(".lat").val() == '' 
      && $(this).find(".lng").val() == '') {
        setTimeout(function() { 
          geocodeLocation($(this))
        }.bind(this), index * time);                    
   }
 });
guest271314
  • 1
  • 15
  • 104
  • 177
  • Hmm, never seen that construct. Looks interesting. I wonder why I see a number object in the console and not a primitive... I will try that with a jQuery collection - that works. Wonderful - http://jsfiddle.net/mplungjan/36d7b2ar/ – mplungjan Jun 12 '15 at 05:04
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/80346/discussion-between-guest271314-and-mplungjan). – guest271314 Jun 12 '15 at 05:07
  • Aha: bind isn't supported in IE8 and Safari5 – mplungjan Jun 12 '15 at 05:29
  • Not sure. I am off my computer now – mplungjan Jun 12 '15 at 05:37