0

Hoping someone out there can offer an incline of insight/advice on how to overcome my coding issue?

I am trying to update a global scope object var, locally within a Promise.then() function. The data comes from an element in the DOM which has been manipulated inside another function as part of the $.when procedure.

Inside the .then() I can console.log the element and verify that the element has been updated and is such within the local scope, but when I try to pass that data to "placeIDs" object and console.log that it sometimes works and sometimes shows as undefined.

It, to me seems to have defied normal logic unless my brain is cooked and I am not seeing the obvious.

My code is below (it is all part of a Google maps API script), please ask if you need any answers in order to help, good luck and thank you.

  var target   = null,
      placeIDs = {

          originPlaceId:      null,
          destinationPlaceId: null

      }
  ;

  // Render the direction route for the map
  $.when(calculateAndDisplayRoute(map, me, directionsService, directionsDisplay)).then(function(response) {

    const target = $('.site-content');

    const placeIDs = {

      originPlaceId:      target.attr('data-orig'),
      destinationPlaceId: target.attr('data-dest')

    }

    // Add the autocomplete function for the map
    new AutocompleteDirectionsHandler(map, directionsService, directionsDisplay, placeIDs.originPlaceId, placeIDs.destinationPlaceId);

  }).catch(

    (reason) => {

      // Log the rejection reason
      console.log('Handle rejected ' + reason + ' promise.');

  });

As requested the promise function below:

function calculateAndDisplayRoute(map, me, directionsService, directionsDisplay) {

    if (me == null) {

        var travelModeChosen = 'DRIVING';

    } else {

        var travelModeChosen = me.travelMode;

    }

    placeIDs = directionsService.route({

        origin:       document.getElementById('origin-input').value,
        destination:  document.getElementById('destination-input').value,
        travelMode:   travelModeChosen

    }, function(response, status) {

        if (status === 'OK') {

            // Clear the direction text panel
            $('#right-panel').empty();

            // Insert place IDs into the DOM
            $('.site-content')
                .attr('data-orig', response.geocoded_waypoints[0]['place_id'])
                .attr('data-dest', response.geocoded_waypoints[1]['place_id'])
            ;

            directionsDisplay.setPanel(document.getElementById('right-panel'));
            directionsDisplay.setDirections(response);

            if (travelModeChosen == 'DRIVING') {

                var trafficLayer = new google.maps.TrafficLayer();
                trafficLayer.setMap(map);

            } else if (travelModeChosen == 'TRANSIT') {

                var transitLayer = new google.maps.TransitLayer();
                transitLayer.setMap(map);

            } else if (travelModeChosen == 'BYCYCLE') {

                var bikeLayer = new google.maps.BicyclingLayer();
                bikeLayer.setMap(map);

            }

            // Set up the map for the direction route and pins
            directionsDisplay.setMap(null);
            directionsDisplay.setMap(map);

        } else {

            window.alert('Directions request failed due to ' + status);

        }

    });

}
zealisreal
  • 495
  • 2
  • 8
  • 20
  • By declaring `const placeIDs`, you will *never* update the global variable. – Bergi Dec 08 '17 at 14:33
  • "*The data comes from an element in the DOM which has been manipulated inside another function as part of the $.when procedure.*" - why not rather use the `response` for that? Don't use the DOM for transferring data. – Bergi Dec 08 '17 at 14:34
  • @Bergi So the response callback will have the return value of the $.when function? Also, I have tried the placeIDs as a var placeIDs; and let placeIDs; neither of them work either. – zealisreal Dec 08 '17 at 14:51
  • The `then` callback will get as the `response` parameter whatever value the promise fulfilled with. You didn't post the code of `calculateAndDisplayRoute` unfortunately, but that's where you should pass the result data. – Bergi Dec 08 '17 at 14:59
  • 1
    I'm actually trying to say that you should not try to use a global variable at all. – Bergi Dec 08 '17 at 15:00
  • @Bergi Got you, OK, so if I was to console.log(response); then that would have the data from the promise and I should be able to use that inside the .then()? I can add the contents of the promise function if it helps? – zealisreal Dec 08 '17 at 15:11
  • 1
    Yes, you *are* able to log that variable, I just don't know what it holds. It *should* be used to pass the result data, not the DOM. If it doesn't hold the data, you will need to fix it in the `calculateAndDisplayRoute` function. – Bergi Dec 08 '17 at 15:15
  • 2
    calculateAndDisplayRoute does not return a promise but immediately returns undefined `$.when(undefined)` you should return `new Promise` and in your `directionsService.route` resolve or reject it. – HMR Dec 08 '17 at 15:30
  • 1
    Ouch. That function doesn't appear to use promises at all, so `$.when` probably won't work either. What is `directionsService.route`? – Bergi Dec 08 '17 at 15:30
  • Example of converting callback to promise here: https://stackoverflow.com/a/47678417/1641941 – HMR Dec 08 '17 at 15:32
  • OK, this is when my JS knowledge really get stretched to the max and past! :( I do not want to be that person but any chance any of you can edit the code to reflect those recommendations, please? `directionsService.route` is a Google maps api call. – zealisreal Dec 08 '17 at 15:36

1 Answers1

1

As requested in comments, please let me know if you need help. I'd advice you to read into promises or at least watch the video to understand why, the how gets easier when you understand why JS uses promises:

function calculateAndDisplayRoute(map, me, directionsService, directionsDisplay) {
  const travelModeChosen = (me||{}).travelMode || "DRIVING";

  return new Promise(
    (resolve,reject)=>
      directionsService.route(
        {
          origin: document.getElementById('origin-input').value,
          destination: document.getElementById('destination-input').value,
          travelMode: travelModeChosen
        }
        ,(response,status)=>
          status==="OK"
            ? resolve(response)
            : reject(status)
      )

  )
  .then(
    response => {
      $('#right-panel').empty();

      // Insert place IDs into the DOM
      $('.site-content')
        .attr('data-orig', response.geocoded_waypoints[0]['place_id'])
        .attr('data-dest', response.geocoded_waypoints[1]['place_id'])
        ;

      directionsDisplay.setPanel(document.getElementById('right-panel'));
      directionsDisplay.setDirections(response);

      if (travelModeChosen == 'DRIVING') {

        var trafficLayer = new google.maps.TrafficLayer();
        trafficLayer.setMap(map);

      } else if (travelModeChosen == 'TRANSIT') {

        var transitLayer = new google.maps.TransitLayer();
        transitLayer.setMap(map);

      } else if (travelModeChosen == 'BYCYCLE') {

        var bikeLayer = new google.maps.BicyclingLayer();
        bikeLayer.setMap(map);

      }

      // Set up the map for the direction route and pins
      directionsDisplay.setMap(null);
      directionsDisplay.setMap(map);
    }
    ,reject =>
      window.alert('Directions request failed due to ' + reject)
  )

}
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Thank you for this, I shall try the code on Monday and all being well it will be sorted and I shall accept this. I have read several promise articles and ones on MDN but not a JS developer I find it a hard read, but get the basics, i think :) – zealisreal Dec 08 '17 at 16:44
  • 1
    @zealisreal You should really watch the video, it explains more how JS is concurrent with your code only running on one thread. It covers call stack and queue in an easy to understand way: https://youtu.be/8aGhZQkoFbQ – HMR Dec 08 '17 at 17:39
  • I will definitely watch that video thank you for suggesting it. I have finally found time to test your code out. First issue is that the param response has an error as not defined on `,(response,status)=>` line. Any ideas? – zealisreal Dec 11 '17 at 13:19
  • Sorry the above comment; which i cannot now edit, should state that `resolve` is not defined after `status === "OK"` as the issue. – zealisreal Dec 11 '17 at 13:51
  • Hold the press, no need to investigate, I got the fix. i realised the Promise params was using response which would not be available at that point and changed it to resolve and hey presto it work brilliantly. Thanks again for the help and that video was good too. I'll accept the answer :) but for future readers you may want to edit your answer with the fix in so others can use it? – zealisreal Dec 11 '17 at 14:15
  • 1
    @zealisreal Yes, sorry for the typo, fixed the answer. – HMR Dec 11 '17 at 14:34