0

I have an asynchronous loop that sends the index value to save the results. However, for some reason, the first time this item loops, it records the value properly. The second time it loops. it changes the value in the first loop and the value in the second loop, and so forth and so on. It seems to do this inconsistently depending on the input.

Below is the code

promises[address_key] = addresses[address_key].nearest.map(function(currentValue, charger_key) { // This loops through items creating promises
  return new Promise(function(resolve) {
    addresses[address_key].nearest[charger_key].directions = get_driving_directions(addresses[address_key].nearest[charger_key]);
    addresses[address_key].nearest[charger_key].map_img = get_static_map_url(addresses[address_key].nearest[charger_key]);
    get_driving_distance(address_key, charger_key, resolve); // this calls the get_driving_distance function below
  });
});
/* more code here */
// Calls distance matrix from Google's api. 
function get_driving_distance(address_key, charger_key, resolve) {
    var distance_matrix = new google.maps.DistanceMatrixService();
    distance_matrix.getDistanceMatrix( // calls distance matrix in Google API
      {
        origins: [addresses[address_key].address],
        destinations: [new google.maps.LatLng(addresses[address_key].nearest[charger_key].latitude, addresses[address_key].nearest[charger_key].longitude)],
        unitSystem: google.maps.UnitSystem.IMPERIAL,
        travelMode: 'DRIVING'
      }, process_distance_matrix(address_key, charger_key, resolve) // calls process_distance_matrix function sending over variables necessary.
    );
  }
  // processes data from the distance matrix in the function get_driving_distance

function process_distance_matrix(address_key, charger_key, callback) {
  return function(response, status) {
    if (response.rows[0].elements[0].status == 'OK') {
      console.log("response", response, 'status', status, 'address_key', address_key, 'charger_key', charger_key);
      console.log("Records before:", addresses[0].nearest[0].distance, response.rows[0].elements[0].distance.text, 'address_key', address_key, 'charger_key', charger_key);
      // Update the global variable with the distance data. This is recording data in wrong fields.
      addresses[address_key].nearest[charger_key].distance = {
        'text': response.rows[0].elements[0].distance.text,
        'number': response.rows[0].elements[0].distance.value / 1609.344,
        'over_limit': (max_driving_distance ? (response.rows[0].elements[0].distance.value / 1609.344 > max_driving_distance) : false)
      };
      console.log("Records after:", addresses[0].nearest[0].distance, response.rows[0].elements[0].distance.text, 'address_key', address_key, 'charger_key', charger_key);
    } else {
      display_error(address_key + ') ' + addresses[address_key].address + ' - Error getting driving distance');
      addresses[address_key].errors.push('Error getting driving distance');
      progress_status.error++;
    }
    callback();
  }
}

The rest of the code is too much to post, so here is a link to the rest of the code: https://jsfiddle.net/RobertoMejia/cqyyLh27/

The original loop is a for loop on line 68. This loops through addresses and passes address_key to refer to the global object.

There is a second loop on line 183. It is a .map loop. This runs through chargers, and passes charger_key to refer to the global object.

Notice the console.logs in the middle of the function. Those are to show how the variable changes where it shouldn't. The display the object in question before and after the declaration each time. It also shows the address key and the charger key at the time of execution.

Any help would be appreciated.

Berto
  • 138
  • 1
  • 6
  • Where is the loop in this code? – Barmar Dec 21 '16 at 23:21
  • There's way too much code at the fiddle to try to find the problem, but I'll bet it's something like http://stackoverflow.com/questions/1451009/javascript-infamous-loop-issue – Barmar Dec 21 '16 at 23:26
  • @Barmar Yes, it is a bit complicated. The loop is on line 183 to 189. – Berto Dec 21 '16 at 23:39
  • I don't see a loop there. There's no `for` or `while` statement, or a call to `forEach()`. – Barmar Dec 21 '16 at 23:41
  • Ahh, now I see the call to `.map()`. – Barmar Dec 21 '16 at 23:42
  • I thought it may be Closures, but if you see the console logs on the console, it displays the correct index. That is what is throwing me off If it makes it easier, just focus on the lines that I added in the question itself. That includes the loop, and the code the loop affects. Either way, I really appreciate the help. – Berto Dec 21 '16 at 23:47
  • I'm not really sure what I'm seeing there. Another thing to remember is that objects displayed with `console.log()` are live. If you modify the object after logging it, and then expand the object in the log, you'll see its new contents. To avoid this problem, use `console.log(JSON.stringify(object))`, then you'll get a snapshot of it. – Barmar Dec 21 '16 at 23:50
  • Oh, ok. sorry. Records before and Records after shows the object saved on line 226, the address key, and the charger key. The address key is the one we are concerned with. It is the one that we are looping through. I added that basically to show how it changes after that call. That is the part where I got stuck. It shows the correct index number, but it records the correct number and other numbers too. – Berto Dec 21 '16 at 23:59
  • You're assigning to `addresses[address_key]`. But you're logging `addresses[0]` in the `Records before` and `Records after` log messages. – Barmar Dec 22 '16 at 00:16
  • Well, the problem is that addresses[0] gets assigned the correct value at first, but gets assigned the value for addresses[1] the next loop around. The end result is [0] and [1] have the result for [1], [2] and [3] have the result for [3]. and [4] has the result for [4]. I set addresses[0], so I can try to see why it changes from the correct value the second time around. – Berto Dec 22 '16 at 04:10
  • I haven't figured out why, but the problem is that when it's processing response #1, `addresses[1].nearest[0]` is the same object as `addresses[0].nearest[0]`. But the containing objects aren't the same, so the problem has to do with where `nearest[0]` comes from -- it's assigning a reference to an object that has been used before, instead of a new object. – Barmar Dec 22 '16 at 06:54
  • The way to debug this isn't with lots of console.log statements. Set breakpoints in the Javascript debugger, single-step through the program, and examine objects in the console. I found this by setting a breakpoint in `process_distance_matrix` and typing `addresses[1].nearest[0] == addresses[0].nearest[0]`, and it said they're equal. – Barmar Dec 22 '16 at 06:57

1 Answers1

3

I think the problem is here in process_addresses:

addresses[address_key].nearest = charger_data.sort( sort_by_closest( addresses[address_key].geo ) );
    // Takes the top results based on the number of results wanted.
addresses[address_key].nearest = addresses[address_key].nearest.slice(0, $('#number_of_results').val() );

If the same charger is near multiple addresses, that charger will be in the nearest array for all of them. So when you add the driving directions to the charger from one address, you're replacing the directions from the previous address.

There are two solutions:

The simplest is to clone the charger objects before putting them into the nearest array.

addresses[address_key].nearest = addresses[address_key].nearest.slice(0, $('#number_of_results').val()).map(function(charger) {
    return $.extend({}, charger);
});

Another way is to use a different object to hold the directions, and make the charger a property of it:

addresses[address_key].nearest = addresses[address_key].nearest.slice(0, $('#number_of_results').val()).map(function(charger) {
    return { charger: charger };
});

This is an application of Wheeler's famous aphorism: "All problems in computer science can be solved by another level of indirection"

Modified fiddle (using the first solution)

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Thank you, Barmar! That worked! You are a code Jedi!!! I will help spread the word of your amazing skills and the good deed you have done today! Thanks again!! – Berto Dec 22 '16 at 19:08