简体   繁体   中英

Why global variable inside for loop doesn't change value? Javascript

I have a problem when executing a function that runs a for loop with 3 global variables that will be passed to a callback function. All the arrays have the same length, the problem comes with the 3 window variables, called in getPickup() inside the callback function don't change value in any of the loop goes. I find it strange because those variables should get a new value updated from location when the callback function is finished, and the loop is run again.

function calculateDistanceDriverCustomer() {
       {% autoescape off %}
       var locations = {{ locations }}
       {% endautoescape %}

       {% autoescape off %}
       var firstname = {{ firstname }}
       {% endautoescape %}

       {% autoescape off %}
       var lastname = {{ lastname }}
       {% endautoescape %}

       {% autoescape off %}
       var rating = {{ rating }}
       {% endautoescape %}
       var location;
       for (location = 0; location < locations.length; location++) {
        var origin = locations[location];
        window.firstname = firstname[location];
        window.lastname = lastname[location];
        window.rating = rating[location];
        var destination = $('#origin-input').val();
        var service = new google.maps.DistanceMatrixService();
        service.getDistanceMatrix(
                {
                    origins: [origin],
                    destinations: [destination],
                    travelMode: google.maps.TravelMode.DRIVING,
                    unitSystem: google.maps.UnitSystem.METRIC, // kilometers and meters.
                    avoidHighways: false,
                    avoidTolls: false
                }, callback);
        }
};

function callback(response, status) {
            if (status != google.maps.DistanceMatrixStatus.OK) {
                $('#result').html(err);
            } else {
                var origin = response.originAddresses[0];
                var destination = response.destinationAddresses[0];
                if (response.rows[0].elements[0].status === "ZERO_RESULTS") {
                    $('#result').html("Better get on a plane. There are no roads between "  + origin + " and " + destination);
                } else {
                        //get estimated pickup time if distance less or equal than 10km
                    function getPickup() {
                        var distance = response.rows[0].elements[0].distance;
                        var duration = response.rows[0].elements[0].duration;
                        var distance_in_kilo = distance.value / 1000;
                        var duration_value = duration.value*1000;
                        console.log(distance_in_kilo);
                        var time = new Date()
                        time = new Date(time.getTime() + duration_value);
                        var date = new Date(time)
                        var currenthr = date.getHours()
                        var currentmin = date.getMinutes()
                        var currentsec = date.getSeconds()
                        if (currenthr   < 10) {currenthr   = "0"+currenthr;}
                        if (currentmin < 10) {currentmin = "0"+currentmin;}
                        if (currentsec < 10) {currentsec = "0"+currentsec;}
                        window.pickupTime = currenthr + ":" + currentmin + ":" + currentsec;
                        console.log(pickupTime);
                        if (distance_in_kilo <= 3000) {
                            var name = document.createElement("hd");
                            var brk = document.createElement("br");
                            var node = document.createTextNode(window.firstname + " " + 
                            window.lastname);
                            name.appendChild(node);
                            var element = document.getElementById("cars");
                            element.appendChild(name);
                            element.appendChild(brk);
                            var pickup = document.createElement("hd");
                            var node1 = document.createTextNode("Pickup Time: " + window.pickupTime);
                            pickup.appendChild(node1);
                            var element1 = document.getElementById("cars");
                            element1.appendChild(pickup);
                            element1.appendChild(brk);
                            var rating = document.createElement("hd");
                            var node3 = document.createTextNode("rating: " + window.rating);
                            rating.appendChild(node3);
                            var element3 = document.getElementById("cars");
                            element3.appendChild(rating);
                            element3.appendChild(brk);
                        }
                        else {
                            console.log("Not available");
                        }

                     }
                    getPickup();
                }
            }
        };

The variables are defined at the beginning of the for loop, they are window.firstname , window.lastname and window.rating . They get the value from an array whose contents have been passed from python(defined at beginning of calculateDistanceDriverCustomer() . This is the python code:

 cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
    cursor.execute("SELECT CurrentLocation, FirstName, LastName, OverallRating FROM driver WHERE OnJourney=0")
    rows = cursor.fetchall()  # data from database
    locations = []
    firstname = []
    lastname = []
    rating = []
    for row in rows:
        locations.append(row['CurrentLocation'])
        firstname.append(row['FirstName'])
        lastname.append(row['LastName'])
        rating.append(row['OverallRating'])
    return render_template("Search.html", rows=rows, locations=locations, firstname=firstname, lastname=lastname, rating=rating)

Thanks for your help

I have no knowledge of Django, but the getDistanceMatrix callback call is probably made asynchronously. This means that your for-loop is already finished when the first callback is triggered. Resulting in all callbacks using the values window.firstname , window.lastname and window.rating that are set by the last iteration of the loop.

If this is indeed the issue you can resolve it by passing them as callback parameters instead.

for (let location = 0; location < locations.length; location++) {
  // ^^^ Move the location definition inside the for-loop and use `let` instead
  //     of `var`. This has to do with scoping and might prevent bugs later on.               
  // https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example

  // Use `const` (or `let`) instead of `var` see link above.
  const data        = {},
        destination = $('#origin-input').val(),
        service     = new google.maps.DistanceMatrixService();

  data.origin    = locations[location];
  data.firstname = firstname[location];
  data.lastname  = lastname[location];
  data.rating    = rating[location];

  service.getDistanceMatrix(
    {
      origins:       [origin],
      destinations:  [destination],
      travelMode:    google.maps.TravelMode.DRIVING,
      unitSystem:    google.maps.UnitSystem.METRIC, // kilometers and meters.
      avoidHighways: false,
      avoidTolls:    false
    },
    // forward the response and status to the callback and add your data
    (response, status) => callback(response, status, data)
  );
}

Then accept those parameters in in your callback:

function callback(response, status, data) {
  // ...
  var node = document.createTextNode(`${data.firstname} ${data.lastname}`);
  // ...
}

I would also recommend to move things like new google.maps.DistanceMatrixService() and $('#origin-input').val() outside of the for-loop. They don't depend upon the elements being iterated and are created more that needed. (If locations contains 100 elements you create 100 new google.maps.DistanceMatrixService() while you most likely only need 1.) I left this out from the example code, since it isn't necessary to fix your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM