title: Use DirectionServices asynchronously
body: <p>I am a Java developer student and I have a problem understanding how to properly write asynchronous functions in Javascript. I read a lot of tutorials but I don't find a good way to implement proper asynchronous methods all the time.</p>
  <p>I am making an app using the Google Maps API and in a method I want to use the directionsService API in order to retrieve the overview_polyline value out of the result object. The method does what it should but it returns an undefined object before to receive the data.</p>
  <p>The method looks like this :</p>
  <p>&quot;start and &quot;end&quot; variables are both Google Markers</p>
  <pre><code>  async createPolyline(startEndPointsArray){
      //Creates a polyline
      const directionsService = new google.maps.DirectionsService();
      const request = {
        origin: this.start.getPosition(),
        destination: this.end.getPosition(),
        travelMode: google.maps.TravelMode.DRIVING,
        unitSystem: google.maps.UnitSystem.METRIC
      };
  
     const getData = await directionsService.route(request,
        function (result, status) {
          if (status === &quot;OK&quot;) {
            console.log(result.routes[0].overview_polyline)
            return result.routes[0].overview_polyline;
          } else {
            window.alert(&quot;Directions request failed due to &quot; + status);
          }
        });
      getData.then(data =&gt; {return data})
    }
  </code></pre>
  <p>getData remains null so I guess the problem is within the function</p>
  <p>and , for reference, it is called from this method that is also synchronous</p>
  <pre><code> async setChargingStationsMarkers(startEndPointsArray) {
     
      const polyline = await this.createPolyline(startEndPointsArray);
      console.log(polyline);
  
  [some additional code fetching a json response from an external website...]
  
  </code></pre>
  <p><strong>code snippet (from <a href="https://jsfiddle.net/wy2xojba/2/" rel="nofollow noreferrer">fiddle</a> in comments)</strong></p>
  <p><div class="snippet" data-lang="js" data-hide="true" data-console="false" data-babel="false">
  <div class="snippet-code snippet-currently-hidden">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>let map;
  let mapCenter = { lat: 59.428, lng: 24.76};
  let start;
  let end;
  let chargingPointsMarkers = [];
  let markerArray = [];
  let stopoverMarkers = []
  let vehicle1 = {capacity: 33, status: 33, consumption: 6.6666} //1KW = 6.6666 Km; Capacity in KM = status*consumption;
  
  function initMap(listener) {
      //Create the map, the DirectionsService, the DirectionsRenderer and an eventListener for the GO button
      //If I chose to implement a detailed steps display it would also be created here
      const directionsService = new google.maps.DirectionsService();
  
      const mapOptions = {
          center: mapCenter,
          zoom: 7,
      }
      map = new google.maps.Map(document.getElementById("map"), mapOptions);
  
      const directionsRenderer = new google.maps.DirectionsRenderer({map: map});
  
      //const stepDisplay = new google.maps.InfoWindow();
  
      const geocoder = new google.maps.Geocoder();
      document.getElementById("submit").addEventListener("click", () =&gt; {
          launcher(geocoder, directionsRenderer, directionsService);
      });
  }
  
  
  async function launcher(geocoder, directionsRenderer, directionsService){
      //the method is used to be launched by the eventListener
      //it sets up the start and end points, fetches the EV markers and launches the route calculation process though a callback function
      resetMarkers();
      const startEndPointsArray =  await setupRoutingProcess(geocoder);
      await callbackHandler(startEndPointsArray,directionsRenderer,
          directionsService, calculateAndDisplayRoute);
  }
  
  function setMapOnAll(map){
      // Sets the map on all markers in the array.
      for (let i = 0; i &lt; markerArray.length; i++) {
          markerArray[i].setMap(map);
      }
  }
  
  function clearMarkers() {
      // Removes the markers from the map, but keeps them in the array.
      setMapOnAll(null);
  }
  
  function resetMarkers(){
      // Pushes all visible markers to a same array,
      // launches the different reset processes and
      // deletes all markers in the arrays by removing references to them.
      for (let i = 0; i &lt; chargingPointsMarkers.length; i++) {
          markerArray.push(chargingPointsMarkers[i])
      }
      chargingPointsMarkers = [];
  
      for (let j = 0; j &lt; stopoverMarkers.length; j++) {
          markerArray.push(stopoverMarkers[j])
      }
      stopoverMarkers = [];
  
      clearMarkers();
  
      markerArray = []
  }
  
  async function setupRoutingProcess(geocoder){
      //launches the setGeocodeAddress method for both start and end points and stores them in an array
      start = await setGeocodeAddress(geocoder, map, "start");
      end = await setGeocodeAddress(geocoder, map, "end");
      let startEndPointsArray = [start];
      startEndPointsArray.push(end);
      return startEndPointsArray;
  
  }
  
  async function setGeocodeAddress(geocoder, resultsMap, elementId) {
      //Retrieve the addresses (strings) from the html text boxes and uses Geocoder to Google Markers objects.
      //it pushes those markers in an array later used to delete the markers on the map
      const address = document.getElementById(elementId).value;
      return new Promise(resolve =&gt; geocoder.geocode({address: address},
          (results, status) =&gt; {
              if (status === "OK") {
                  resultsMap.setCenter(results[0].geometry.location);
                  const marker = new google.maps.Marker({
                      map: resultsMap,
                      position: results[0].geometry.location,
                      title: elementId,
                  });
                  resolve(marker)
                  markerArray.push(marker);
              } else {
                  alert("Trip Route finder was not successful for the following reason: " + status);
              }
          }));
  }
  
  async function callbackHandler (startEndPointsArray,
                                  directionsRenderer,
                                  directionsService,
                                  calculateAndDisplayRoute){
  
      let jsonChargingPoints = await setChargingStationsMarkers(startEndPointsArray, directionsRenderer,
          directionsService, calculateAndDisplayRoute);
  
  
      await createChargerPointMarkers(jsonChargingPoints)
  
      calculateAndDisplayRoute(
          directionsRenderer,
          directionsService,
          jsonChargingPoints
      );
  }
  
  async function setChargingStationsMarkers(startEndPointsArray, directionsRenderer,
                                            directionsService, calculateAndDisplayRoute) {
      //Creates an encoded polyline to be passed as an Url argument to limit the results
      //fetches the EV Charging Points as Json response
      const polyline = await createPolyline(startEndPointsArray);
      console.log(polyline);
      const baseUrl = 'https://api.openchargemap.io/v3/poi/?output=json&amp;maxresults=200&amp;includecomments=true';
      const queryUrl = baseUrl + '&amp;polyline=' + polyline + '&amp;distance=50';
      let data  = await fetch(queryUrl)
          .then((response) =&gt; response.json())
          .then((data) =&gt; {return data})
      return data;
  }
  
    async function createPolyline(startEndPointsArray){
      //Creates a polyline
      const directionsService = new google.maps.DirectionsService();
      const request = {
        origin: start.getPosition(),
        destination: end.getPosition(),
        travelMode: google.maps.TravelMode.DRIVING,
        unitSystem: google.maps.UnitSystem.METRIC
      };
  
     const getData = await directionsService.route(request,
        function (result, status) {
          if (status === "OK") {
            console.log(result.routes[0].overview_polyline)
            return result.routes[0].overview_polyline;
          } else {
            window.alert("Directions request failed due to " + status);
          }
        });
      console.log(getData)
      getData.then(data =&gt; {return data})
    }
  
  function createChargerPointMarkers(jsonChargingPoints) {
      //Loop through the Json response and launch the PlaceMarkers function
      for (let x = 0; x &lt; jsonChargingPoints.length; x++) {
          const LatLng = new google.maps.LatLng(parseFloat(jsonChargingPoints[x].AddressInfo.Latitude), parseFloat(jsonChargingPoints[x].AddressInfo.Longitude));
          placeMarker(LatLng);
      }
  }
  
  function placeMarker(location) {
      //Convert the Json response elements to Google Markers, places them on the Map and pushes them to an array.
      let marker = new google.maps.Marker({
          position: location,
          map,
          icon: {
              path: google.maps.SymbolPath.BACKWARD_CLOSED_ARROW,
              scale: 3,
          },
          draggable: false,
      });
      markerArray.push(marker)
      chargingPointsMarkers.push(marker)
  }
  
  async function calculateAndDisplayRoute(
      directionsRenderer,
      directionsService,
      jsonChargingPoints,
      stepDisplay,
      map) {
  
      if (!compareVehicleCapacityToDistance(vehicle1, start)) {
          setChargeCheckpoint(vehicle1)
      }
  
      directionsService.route(setRequest(),
          function (result, status) {
              if (status === "OK") {
                  directionsRenderer.setDirections(result);
                  // showSteps(result, markerArray, stepDisplay, map);
              } else {
                  window.alert("Directions request failed due to " + status);
              }
          });
  }
  
  function setRequest(){
      //prepares the request sent to the Directions service
      let stopovers = [];
      for (let x = 0; x &lt; stopoverMarkers.length; x++){
          let latLng = stopoverMarkers[x].getPosition();
          let waypoint = {
              location: latLng,
              stopover: true
          };
          stopovers.push(waypoint)
      }
  
      const request = {
          origin: start.getPosition(),
          destination: end.getPosition(),
          waypoints: stopovers,
          travelMode: google.maps.TravelMode.DRIVING,
          unitSystem: google.maps.UnitSystem.METRIC
      };
      return request;
  }
  
  function compareVehicleCapacityToDistance(vehicle, p1){
      //Checks if the distance to destination is greater than the vehicle capacity
      if (calculateDistance(p1, end) &gt; (vehicle.status*vehicle.consumption)){
          return false
      }return true;
  }
  
  function setChargeCheckpoint(vehicle){
      //launches the function selecting the closest marker to destination
      //Setting a marker of the selected marker on the map (might be redundant)
      //Pushes it to markerArray for later deletion (might be redundant)
      //Pushes it to stopoverMarkers to be used by the Directions service to setup a route
      let waypoint = selectMarkerClosestToDestination(vehicle);
      const waypointLocation = waypoint.getPosition();
      const marker = new google.maps.Marker({
          position: waypointLocation,
          stopover: true,
          draggable: false,
          title: "EV charging stopover"
      });
      markerArray.push(marker)
      stopoverMarkers.push(marker)
  }
  
  
  
  function selectMarkerClosestToDestination(vehicle) {
      //Selecting the closest marker to destination as long as it is not out of the vehicle capacity range
      //CURRENTLY BUGGED
      let waypoints = chargingPointsMarkers;
  
      for (let x = waypoints.length -1; x &gt;= 0; x--) {
          if(calculateDistance(waypoints[x], start) &gt; (vehicle.status*vehicle.consumption)){
              waypoints.splice(x, 1)
          }
      }
  
      for (let x = waypoints.length - 1; x &gt; 0; x--) {
          if (calculateDistance(waypoints[x], end) &gt; (calculateDistance(waypoints[x-1], end))) {
              waypoints.splice(x, 1);
          } else {
              waypoints.splice(x - 1, 1);
          }
      }
      return waypoints[0];
  }
  
  function calculateDistance(p1, p2) {
      //Uses the Google geometry library to calculate distance between two Markers
      let a = p1.getPosition();
      let b = p2.getPosition();
  
      let distance = (google.maps.geometry.spherical.computeDistanceBetween(a, b) / 1000).toFixed(2);
      return distance;
  }
  
  function showSteps(directionResult, stepDisplay, map) {
      // For each step, place a marker, and add the text to the marker's infowindow.
      // Also attach the marker to an array so we can keep track of it and remove it
      // when calculating new routes.
      //NOT CURRENTLY IMPLEMENTED/USED
      const myRoute = directionResult.routes[0].legs[0];
  
      for (let i = 0; i &lt; myRoute.steps.length; i++) {
          const marker = (markerArray[i] =
              markerArray[i] || new google.maps.Marker());
          marker.setMap(map);
          marker.setPosition(myRoute.steps[i].start_location);
          attachInstructionText(
              stepDisplay,
              marker,
              myRoute.steps[i].instructions,
              map
          );
      }
  }
  
  function attachInstructionText(stepDisplay, marker, text, map) {
      google.maps.event.addListener(marker, "click", () =&gt; {
          // Open an info window when the marker is clicked on, containing the text
          // of the step.
          //NOT CURRENTLY IMPLEMENTED/USED
          stepDisplay.setContent(text);
          stepDisplay.open(map, marker);
      });
  }</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>/* Always set the map height explicitly to define the size of the div
         * element that contains the map. */
  #map {
      height: 90%;
  }
  
  /* Optional: Makes the sample page fill the window. */
  html,
  body {
      height: 100%;
      margin: 0;
      padding: 0;
  }
  
  #floating-panel {
      position: absolute;
      top: 10px;
      left: 25%;
      z-index: 5;
      background-color: #fff;
      padding: 5px;
      border: 1px solid #999;
      text-align: center;
      font-family: "Roboto", "sans-serif";
      line-height: 30px;
      padding-left: 10px;
  }
  
  #warnings-panel {
      width: 100%;
      height: 10%;
      text-align: center;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;!DOCTYPE html&gt;
  &lt;html&gt;
  &lt;head&gt;
      &lt;title&gt;EV Trip Route Finder&lt;/title&gt;
      &lt;script src="https://polyfill.io/v3/polyfill.min.js?features=default"&gt;&lt;/script&gt;
      &lt;script
              src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&amp;callback=initMap&amp;libraries=&amp;v=weekly"
              defer
      &gt;&lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
  &lt;div id="floating-panel" &gt;
      &lt;b&gt;Start: &lt;/b&gt;
      &lt;input id="start" type="text" value="Nantes"&gt;
      &lt;b&gt;End: &lt;/b&gt;
      &lt;input id="end" type="text" value="Paris"&gt;
      &lt;input id="submit" type="button" value="GO" /&gt;
  &lt;/div&gt;
  &lt;div id="map"&gt;&lt;/div&gt;
  &lt;div id="warnings-panel"&gt;&lt;/div&gt;
  &lt;/body&gt;
  &lt;/html&gt;</code></pre>
  </div>
  </div>
  </p>