Back

Technologies:

javascriptjavascript
reactjsreactjs
avatar
Tolerim
a month ago

What is the method of pausing a for loop in React while awaiting a return value from an asynchronous function?

My goal is to output the dist variable in order as I iterate through the array, but it's not printing in order. The callback function's for loop seems to execute in order, but the first for loop isn't. I want to know how to make it execute in order. Here's the code:

for (let i = 0; i < waypoints.length; i+=40){
      service.nearbySearch({
        location:{ lat: waypoints[i][0], lng:waypoints[i][1]},
        radius: '20000',
        type: ['gas_station']
      }, callback);

      async function callback(results, status){
        if(status === google.maps.places.PlacesServiceStatus.OK){
          for (var j = 0; j < results.length; j++){
            if(google.maps.geometry.poly.containsLocation(results[j].geometry.location,pBounds) === true){
              let dist = await distanceCalc(distCoords[distCoords.length-1][0], distCoords[distCoords.length-1][1], results[j].geometry.location.lat(), results[j].geometry.location.lng() )
              dist = dist/Mpg
              console.log('i: '+i+'j: ' + j + "Distance: " + dist)
In the above code, the for loop is iterating in an unexpected way. I've attempted to make the function that includes the for loops async, but that didn't work. Here's the method used to calculate the distance:

async function distanceCalc(lat1, long1, lat2, long2){
    const pt1 = {lat: lat1, lng: long1}
    const pt2 = {lat: lat2, lng: long2}

    let distanceService = new google.maps.DirectionsService()
    const dist = await Promise.resolve(distanceService.route({
      origin: pt1,
      destination: pt2, 
      travelMode: google.maps.TravelMode.DRIVING}))
    
    var miles = dist.routes[0].legs[0].distance.value

    miles = miles/1609.34
    return miles
  }
This is an example of the output in the console:

console output example

Answers(2)
avatar
Vogi
a month ago
Verified Answer
The reason why the first for loop is not executing in order is because the nearbySearch function is asynchronous. This means that it does not block the execution of the code and will continue to execute the next line of code before the nearbySearch function has finished executing. Therefore, the callback function will be executed in a non-deterministic order. To make it execute in order, you can use a Promise to wait for each nearbySearch function to finish executing before moving on to the next iteration of the for loop. Here's an example:
async function searchWaypoints(){
  for (let i = 0; i < waypoints.length; i+=40){
    await new Promise(resolve => {
      service.nearbySearch({
        location:{ lat: waypoints[i][0], lng:waypoints[i][1]},
        radius: '20000',
        type: ['gas_station']
      }, (results, status) => {
        if(status === google.maps.places.PlacesServiceStatus.OK){
          for (var j = 0; j < results.length; j++){
            if(google.maps.geometry.poly.containsLocation(results[j].geometry.location,pBounds) === true){
              let dist = await distanceCalc(distCoords[distCoords.length-1][0], distCoords[distCoords.length-1][1], results[j].geometry.location.lat(), results[j].geometry.location.lng() )
              dist = dist/Mpg
              console.log('i: '+i+'j: ' + j + "Distance: " + dist)
            }
          }
        }
        resolve();
      });
    });
  }
}

searchWaypoints();
This code uses an async function and a Promise to wait for each nearbySearch function to finish executing before moving on to the next iteration of the for loop. The resolve() method is called when the nearbySearch function has finished executing, which allows the code to move on to the next iteration of the for loop. I hope this helps! Let me know if you have any other questions.

Sources

Loops and iteration - JavaScript | MDN - Mozilla DeveloperC for Loop (With Examples) - Programizc - What is the order in a for loop? - Stack OverflowIn what order does a C# for each loop iterate over a List<T>?How to make for loop in windows batch file run in name order
avatar
Tolerim
a month ago
The issue in this code is that the nearbySearch function is asynchronous and may return results in any order. Therefore, the callback function may be called with results in a different order than the original input array. One solution to this would be to use the index i as a parameter to the callback function and pass it along to distanceCalc so that it can be used to identify the correct index for each distance calculation. Here's how you could modify the code:
for (let i = 0; i < waypoints.length; i += 40) {
  service.nearbySearch({
    location: { lat: waypoints[i][0], lng: waypoints[i][1] },
    radius: '20000',
    type: ['gas_station']
  }, (results, status) => callback(i, results, status)); // pass i as parameter to callback
}

async function callback(i, results, status) {
  if (status === google.maps.places.PlacesServiceStatus.OK) {
    for (var j = 0; j < results.length; j++) {
      if (google.maps.geometry.poly.containsLocation(results[j].geometry.location, pBounds) === true) {
        let dist = await distanceCalc(
          distCoords[distCoords.length - 1][0],
          distCoords[distCoords.length - 1][1],
          results[j].geometry.location.lat(),
          results[j].geometry.location.lng()
        );
        dist = dist / Mpg;
        console.log('i: ' + i + ' j: ' + j + ' Distance: ' + dist);
      }
    }
  }
}

async function distanceCalc(lat1, long1, lat2, long2, i) {
  const pt1 = { lat: lat1, lng: long1 };
  const pt2 = { lat: lat2, lng: long2 };

  let distanceService = new google.maps.DirectionsService();
  const dist = await Promise.resolve(
    distanceService.route({
      origin: pt1,
      destination: pt2,
      travelMode: google.maps.TravelMode.DRIVING
    })
  );

  var miles = dist.routes[0].legs[0].distance.value;

  miles = miles / 1609.34;
  //console.log("MILES: " + miles)
  return { miles: miles, index: i }; // return an object with miles and index
}
In this modified code, we pass i as a parameter to the callback function and call it with callback(i, results, status). Then, we modify distanceCalc to return an object containing both miles and i. Finally, we modify callback to use the index i when logging the distance so that it matches the original input array order.
;