Problem with AJAX in a for loop : How can i make an ajax call on each element of an array?

Viewed 51

I'm new on StackOverflow :)

I need help: I'm trying to make an inverse geocode (get an address from coordinates). I have a good URL that works with an ajax. I have many coordinates to convert to address, that is why I'm trying firstly on an array with 4 coordinates on JSfiddle.

What I want for the result is that my script adds all the addresses to my array. I'm trying with a for loop, and on each element of the array, an ajax call which finds the good coordinates.

You can find the call on this link (i put the script after the link) : https://jsfiddle.net/e2wr3umy/

my code which works for an element:

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
$.ajax({

                     url: 'https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json',
                      type: 'GET',
                      dataType: 'jsonp',
                      jsonp: 'jsoncallback',
                      data: {
                        prox: '43.564624, 3.847154,250',
                        mode: 'retrieveAddresses',
                        maxresults: '1',
                        gen: '9',
                        apiKey: 'NOT_THE_REAL_API_KEY'
                      },
                      success: function (data) {
                        var re = JSON.stringify(data);
                        console.log(data);
                        var num = (data.Response.View[0].Result[0].Location.Address.HouseNumber);
                        var rue = (data.Response.View[0].Result[0].Location.Address.Street);
                        var ville = (data.Response.View[0].Result[0].Location.Address.City);
                        var CP = (data.Response.View[0].Result[0].Location.Address.PostalCode);
                        console.log(num, rue, CP, ville)
                      }
                    });

Then, I'm trying on an array of 4 elements, this is the link: https://jsfiddle.net/L07pjwba/

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>

var adresse = [['43.564624, 3.847154',1],['43.564624, 3.447154',27],['43.64624, 3.727154',75],['43.564624, 3.617154',254]];


for (i =0; i<adresse.length; i++) {
console.log("element", adresse[i]);
$.ajax({
                      url: 'https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json',
                      type: 'GET',
                      dataType: 'jsonp',
                      jsonp: 'jsoncallback',
                      data: {
                        prox: adresse[i][0].concat(', 250'),
                        mode: 'retrieveAddresses',
                        maxresults: '1',
                        gen: '9',
                        apiKey: 'NOT_THE_REAL_API_KEY'
                      },
                      success: function (data) {
                        var re = JSON.stringify(data);
                        console.log("data", data);
                        var num = (data.Response.View[0].Result[0].Location.Address.HouseNumber);
                        var rue = (data.Response.View[0].Result[0].Location.Address.Street);
                        var ville = (data.Response.View[0].Result[0].Location.Address.City);
                        var CP = (data.Response.View[0].Result[0].Location.Address.PostalCode);
                        adresse[i].push(num, rue, ville, CP);

                      }
                    });

}
console.log("adresse : ",adresse);

But the result is not what I expect: it looks like the ajax call starts after the end of the for loop, even if it is inside the for a loop. I tried with async: false in the ajax, but it didn't change anything.

1 Answers

This is a common problem stemming from not understanding async. Here is a canonical resource for async issues, but I don't think it would resolve all of your problems, so I won't mark it as a duplicate, but please read it.

Basically, your code tells the geocoding service to let you know about the addresses. Then you print the array. Some time in the future, the geocoding service answers you, but the array is already printed, and JavaScript doesn't have a time machine.

The basic principle is, you can never use the value you obtain from an async function anywhere except in a callback to that function (or its promise chain), and functions that are called from there. In your first snippet you do it correctly: you use console.log inside the callback. In the second snippet, you move it outside the callback, and that is the source of your problems.

Here is how to write it correctly. $.ajax returns a "promise", which you can chain using .then, to specify that something should be done when the promise is fulfilled (i.e. you get a response from the geocoding service). You can use Promise.all to wait for all the promises in an array to be fulfilled. All together, it should look like this (I changed the minimal amount of your code, I'd write it a little bit differently):

var adresse = [
  ['43.564624, 3.847154', 1],
  ['43.564624, 3.447154', 27],
  ['43.64624, 3.727154', 75],
  ['43.564624, 3.617154', 254]
];

var promises = [];
for (i = 0; i < adresse.length; i++) {
  console.log("element", adresse[i]);
  var promise = $.ajax({
    url: 'https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json',
    type: 'GET',
    dataType: 'jsonp',
    jsonp: 'jsoncallback',
    data: {
      prox: adresse[i][0].concat(', 250'),
      mode: 'retrieveAddresses',
      maxresults: '1',
      gen: '9',
      apiKey: 'NOT_THE_REAL_API_KEY'
    },
  }).then(function(data) {
    var num = (data.Response.View[0].Result[0].Location.Address.HouseNumber);
    var rue = (data.Response.View[0].Result[0].Location.Address.Street);
    var ville = (data.Response.View[0].Result[0].Location.Address.City);
    var CP = (data.Response.View[0].Result[0].Location.Address.PostalCode);
    adresse[i].push(num, rue, ville, CP);
  });
  promises.push(promise);
}

Promise.all(promises).then(function() {
  console.log("adresse : ", adresse);
});
Related