我是一名 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>