0

我是一名 Java 开发人员学生,但在理解如何在 Javascript 中正确编写异步函数时遇到了问题。我阅读了很多教程,但始终找不到实现正确异步方法的好方法。

我正在使用 Google Maps API 制作一个应用程序,并且在一种方法中我想使用 DirectionsService API 以便从结果对象中检索 overview_polyline 值。该方法做了它应该做的,但它在接收数据之前返回一个未定义的对象。

该方法如下所示:

“开始”和“结束”变量都是 Google 标记

  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 === "OK") {
          console.log(result.routes[0].overview_polyline)
          return result.routes[0].overview_polyline;
        } else {
          window.alert("Directions request failed due to " + status);
        }
      });
    getData.then(data => {return data})
  }

getData 保持为空,所以我猜问题出在函数内

并且,作为参考,它是从这个也是同步的方法中调用的

 async setChargingStationsMarkers(startEndPointsArray) {
   
    const polyline = await this.createPolyline(startEndPointsArray);
    console.log(polyline);

[some additional code fetching a json response from an external website...]

代码片段(来自评论中的小提琴

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", () => {
        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 < 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 < chargingPointsMarkers.length; i++) {
        markerArray.push(chargingPointsMarkers[i])
    }
    chargingPointsMarkers = [];

    for (let j = 0; j < 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 => geocoder.geocode({address: address},
        (results, status) => {
            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&maxresults=200&includecomments=true';
    const queryUrl = baseUrl + '&polyline=' + polyline + '&distance=50';
    let data  = await fetch(queryUrl)
        .then((response) => response.json())
        .then((data) => {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 => {return data})
  }

function createChargerPointMarkers(jsonChargingPoints) {
    //Loop through the Json response and launch the PlaceMarkers function
    for (let x = 0; x < 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 < 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) > (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 >= 0; x--) {
        if(calculateDistance(waypoints[x], start) > (vehicle.status*vehicle.consumption)){
            waypoints.splice(x, 1)
        }
    }

    for (let x = waypoints.length - 1; x > 0; x--) {
        if (calculateDistance(waypoints[x], end) > (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 < 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", () => {
        // 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);
    });
}
/* 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;
}
<!DOCTYPE html>
<html>
<head>
    <title>EV Trip Route Finder</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <script
            src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=&v=weekly"
            defer
    ></script>
</head>
<body>
<div id="floating-panel" >
    <b>Start: </b>
    <input id="start" type="text" value="Nantes">
    <b>End: </b>
    <input id="end" type="text" value="Paris">
    <input id="submit" type="button" value="GO" />
</div>
<div id="map"></div>
<div id="warnings-panel"></div>
</body>
</html>

4

1 回答 1

1

方向服务是异步的(就像地理编码器一样)。发布的代码以不同的方式处理这两种服务。处理方向服务的方式与使用地理编码器解决问题的方式相同。

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
  };
  return new Promise(resolve => directionsService.route(request,
    function(result, status) {
      if (status === "OK") {
        resolve(result)
      } else {
        window.alert("Directions request failed due to " + status);
      }
    })
  );
}

像这样称呼它:

const polyline = await createPolyline(startEndPointsArray);
console.log(polyline);

概念证明小提琴

代码片段:

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 geocoder = new google.maps.Geocoder();
  document.getElementById("submit").addEventListener("click", () => {
    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 < 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 < chargingPointsMarkers.length; i++) {
    markerArray.push(chargingPointsMarkers[i])
  }
  chargingPointsMarkers = [];

  for (let j = 0; j < 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 => geocoder.geocode({
      address: address
    },
    (results, status) => {
      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&maxresults=200&includecomments=true';
  const queryUrl = baseUrl + '&polyline=' + polyline + '&distance=50';
  let data = await fetch(queryUrl)
    .then((response) => response.json())
    .then((data) => {
      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
  };
  return new Promise(resolve => directionsService.route(request,
    function(result, status) {
      if (status === "OK") {
        console.log(result.routes[0].overview_polyline)
        resolve(result.routes[0].overview_polyline)
      } else {
        window.alert("Directions request failed due to " + status);
      }
    })
    );
}

function createChargerPointMarkers(jsonChargingPoints) {
  //Loop through the Json response and launch the PlaceMarkers function
  for (let x = 0; x < 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);
      } 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 < 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) > (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 >= 0; x--) {
    if (calculateDistance(waypoints[x], start) > (vehicle.status * vehicle.consumption)) {
      waypoints.splice(x, 1)
    }
  }

  for (let x = waypoints.length - 1; x > 0; x--) {
    if (calculateDistance(waypoints[x], end) > (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 < 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", () => {
    // 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);
  });
}
/* Always set the map height explicitly to define the size of the div
       * element that contains the map. */
#map {
    height: 100%;
}

/* 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;
}
<!DOCTYPE html>
<html>
<head>
    <title>EV Trip Route Finder</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <script
            src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=&v=weekly"
            defer
    ></script>
</head>
<body>
<div id="floating-panel" >
    <b>Start: </b>
    <input id="start" type="text" value="Nantes">
    <b>End: </b>
    <input id="end" type="text" value="Paris">
    <input id="submit" type="button" value="GO" />
</div>
<div id="map"></div>
&nbsp;
<div id="warnings-panel"></div>
</body>
</html>

于 2020-12-16T23:29:09.390 回答