I'm new to javascript, i wanted to check some data using google maps api but this asynchronuous execution of function is killing me, and i don't really understand how callbacks work. Tried to do it but doesn't work.
function initMap(a, callback) {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix({
origins: [punktpocz[a], punktpocz[a + 1]],
destinations: [punktkonc[a], punktkonc[a + 1]],
travelMode: 'DRIVING',
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, function (response, status) {
if (status !== 'OK') {
alert('Error was: ' + status);
} else {
var originList = response.originAddresses;
var destinationList = response.destinationAddresses;
for (var i = 0; i < originList.length; i++) {
var results = response.rows[i].elements;
for (var j = 0; j < results.length; j++) {
theDiv.innerHTML += originList[i] + ' to ' + destinationList[j] +
': ' + results[j].distance.text + ' in ' +
results[j].duration.text + '<br>';
wynik.push({ odl: results[j].distance.text, czas: results[j].duration.text });
}
}
callback();
}
});
}
and in the second function:
for(i=0;i<czasmax;i++){
...
punktpocz.push(kie[id-1]);
punktkonc.push(biz[parseInt(zleceniajava[z+1])]);
punktpocz.push(biz[parseInt(zleceniajava[z+1])]);
punktkonc.push( zle[ parseInt(zleceniajava[z])] );
initMap(i*2,function(){
console.log('huzzah, I\'m done!');
});
...
}
and i want for the script to wait until initmap is finished to continue but it doesn't seem to work.
You might look into using promises (I had a similar question before: Javascript Promise/Then Example) or use async.js
(Node.js | MongoDB count(): Getting Count Before and After Inserting Data)
Related
My goal is to automate process of route calculation from A to B in Google Maps by using JavaScript + Google Distance Matrix Service. I would like to find route which is the fastest (based on current traffic). Script should calculate route by using current date and time.
<script src="https://maps.google.com/maps/api/js?sensor=false"></script>
<script>
function init() {
var service = new google.maps.DistanceMatrixService;
var origin = 'Great,Lake,8';
var destination = 'Lake,Great,21';
service.getDistanceMatrix({
origins: [origin],
destinations: [destination],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
drivingOptions: {
departureTime: new Date(Date.now()), // for the time N milliseconds from now.
trafficModel: "best_guess"
}
}, function(response, status) {
if (status !== google.maps.DistanceMatrixStatus.OK) {
alert('Error was: ' + status);
} else {
<!-- The same accessing of elements in object
-->
<!-- document.getElementById("result").innerHTML += '1)' + JSON.stringify(response.rows[0].elements[0].distance.text) + '<br>';
document.getElementById("result").innerHTML += '2)' + JSON.stringify(response.rows[0].elements[0]['distance']['text']) + '<br>';;
document.getElementById("result").innerHTML += '3) RESPONSE: ' + JSON.stringify(response) +'<br><br>'; -->
document.getElementById("result").innerHTML += 'RESULT: ' + JSON.stringify(response);
alert(response.originAddresses[0] + ' ' + response.destinationAddresses[0] + ' ==> ' + response.rows[0].elements[0].duration.text + ' ' + response.rows[0].elements[0].distance.text);
}
});
}
</script>
<body onload="init()">
<div id="result">
</div>
</body>
Code works only if drivingOptions is removed. But I need this object - because I would like to calculate route based on traffic situation. I am executing script in https://jsfiddle.net/ - so I don't see any errors - except that when I removed drivingOptions object I saw that pop up with time and distance shown in pop up.
I have a for loop and inside that I am calling one function. But before function response for loop variable is incremented by 1.
I just want to wait until the response has come, then increment var by 1.
Here is my code:
for(var i=0;i<$scope.latitude.length;i++)
{
console.log("inside for :",$scope.latitude[i]);
console.log("i after for:" ,i);
console.log("count before if:" ,count);
if($scope.latitude[i]!=1)
{
console.log("inside if count is :",count);
console.log("i inside if:" ,i);
var lat =$scope.latitude[i];
var lng=$scope.longitude[i];
console.log("lat is going",lat);
console.log("lng is going",lng);
$scope.getDistance(lat,lng).then(function(response,flag){
console.log("flag is" ,flag);
setTimeout(function(){
flag="true";
},1000);
var results = response.rows[0].elements;
console.log(results[0].distance.text);
console.log(results[0].duration.text);
return results[0].distance.text
}).done(function(distanceMatrixResult,flag) {
console.log("flag inside done is:" ,flag);
console.log("count is ",count);
console.log("i inside done:" ,i);
console.log(distanceMatrixResult);
$scope.clients.treasureHunts[count].distance =distanceMatrixResult;
$ionicLoading.hide();
console.log("Results:->>>>>>>>>>>>", JSON.stringify( $scope.clients));
})
$scope.getDistance=function(lat,lng){
console.log("lat inside google distance function:-" ,lat);
console.log("lng inside google distance function:-" ,lng);
var service = new google.maps.DistanceMatrixService();
$scope.destinationDetails = {
lat: lat,
lng: lng
}
console.log("destination:-" ,JSON.stringify( $scope.destinationDetails.lat));
console.log("destination:-" ,JSON.stringify( $scope.destinationDetails.lng));
//getting current location
var onSuccess = function (position) {
console.log('Current Position: ', position.coords.latitude, ',', position.coords.longitude);
$scope.originCenter = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
geocodeLatLng($scope.originCenter);
}
function onError(error) {
console.error('code: ' + error.code + '\n' + 'message: ' + error.message + '\n');
//alert('Error Occured' + error.message);
}
navigator.geolocation.getCurrentPosition(onSuccess, onError);/////////---------------------------------
function geocodeLatLng(originCenter) {
console.log("destination inside function:-" ,JSON.stringify( $scope.destinationDetails.lat));
console.log("destination inside function:-" ,JSON.stringify( $scope.destinationDetails.lng));
$scope.originDetails = new google.maps.LatLng(originCenter.lat, originCenter.lng);
console.log('Origin Details: ',JSON.stringify($scope.originDetails));
$scope.finalDestination = new google.maps.LatLng($scope.destinationDetails.lat, $scope.destinationDetails.lng);
console.log('destination details: ', JSON.stringify($scope.finalDestination));
service.getDistanceMatrix(
{
origins: [$scope.originDetails],
destinations: [$scope.finalDestination],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
},
function (response, status) {
if (status !== google.maps.DistanceMatrixStatus.OK) {
console.error(JSON.stringify(response));
d.reject(response);
} else {
console.log("returning response ",JSON.stringify(response));
var flag="true";
d.resolve(response,flag);
}
});
Since js is single threaded (as far as regarded here) there can not be a loop waiting for something other happening in js code (like any callbacks executed..).
Looping over asynchronous calls need to split functionality into the async callback/success-handler (add some error handling as well;), e.g. like this:
function makeNextCall(i, total, onCompleted) {
if( i >= total) {
onCompleted(); // signal completion of whole iteration
} else {
makeAsyncCall(i, { // make the asynchronous call
onSuccess : function() { // define callback
makeNextCall(i+1, total, onCompleted); // next iteration
}
});
}
}
So you have probably seen something similar alot, where someone has a problem with using the "i" variable in a functions inside a for loop. Now that can easily be fixed with:
(function(){
return function() {
//something
}
})(i);
But, how do I do this in my scenario?
GMap.prototype.drawDirection = function (directionsRenderer, directionsService, headMarker, tailMarkers, callback) {
var request;
var array = [];
var count = 0;
for (var i = 0; i < tailMarkers.length; i++) {
count = i;
request = {
origin: headMarker.getPosition(),
destination: tailMarkers[i].getPosition(),
travelMode: google.maps.DirectionsTravelMode.DRIVING,
unitSystem: google.maps.DirectionsUnitSystem.METRIC
};
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(response);
console.log(i + " " + count);
callback(response.routes[0].legs[0].distance.value, i, tailMarkers.length - 1);
} else {
alert('Error: ' + status);
}
});
}
};
To be more accurate, it's about the directionsService object:
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(response);
console.log(i + " " + count);
callback(response.routes[0].legs[0].distance.value, i, tailMarkers.length - 1);
} else {
alert('Error: ' + status);
}
});
Now, I don't want to use JFiddle since it would be alot to write, so I'm just going to link to my site where I'm trying this.
http://stud.aitel.hist.no/~andersfy/html5.proj/
If you look at the checkbox in the bottom right corner, where it says "vis min posisjon" you just need to click on that, and you'll see the problem.
The code is in the file galled GMap.js at line 134 to 142.
I hope I'm giving you enough information!
You should not create functions inside of for loops for this very reason.
Your problem is in this closure, that shares the variable i:
function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(response);
console.log(i + " " + count);
callback(response.routes[0].legs[0].distance.value, i, tailMarkers.length - 1);
} else {
alert('Error: ' + status);
}
}
I would rewrite that function to not depend on i, but if you need i then
/*before your for loop */
function createRouter(i){
return function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(response);
console.log(i + " " + count);
callback(response.routes[0].legs[0].distance.value, i, tailMarkers.length - 1);
} else {
alert('Error: ' + status);
}
}
};
/* in your for loop */
directionsService.route(request, createRouter(i));
I have the following code for directions from google maps apiv3. This part is working good. If i have waypoints in my trip, at the top of each trip, it is showing the time as well as distance for the trip.
I used a variable totadistance to add the distance from all the legs but, it does nothing. I do not see a alert message when i run the application.
I want to see the total time and distance for all the trips. How can i get that information?
function calcRoute(startaddr, endaddr) {
var start = document.getElementById(startaddr).value;
var end = document.getElementById(endaddr).value;
var waypts = [];
var waypointstring;
var waypoint1 = document.getElementById('txtWaypoint').value;
waypointstring= waypoint1.split(";");
//alert("Waypoint Length:" + waypointstring.length)
for (var i = 0; i < waypointstring.length; i++) {
waypts.push({location:waypointstring[i], stopover:true});
}
var request = {
origin: start,
destination: end,
waypoints: waypts,
optimizeWaypoints: true,
travelMode: google.maps.TravelMode.DRIVING };
var totaldistance=0;
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById('directions_panel');
summaryPanel.innerHTML = "";
// For each route, display summary information.
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
summaryPanel.innerHTML += '<b>Route Segment: ' + routeSegment + '</b><br>';
summaryPanel.innerHTML += route.legs[i].start_address + ' to ';
summaryPanel.innerHTML += route.legs[i].end_address + '<br>';
summaryPanel.innerHTML += route.legs[i].distance.text + '<br><br>';
totaldistance = totaldistance + route.legs[i].distance.text ;
}
alert(totaldistance);
}
});
}
Thank you
This is not a number:
totaldistance = totaldistance + route.legs[i].distance.text;
This works to give me the total distance in km:
totaldistance = totaldistance + route.legs[i].distance.value;
working example
I am trying to have a function for calculating routes for google maps be dynamically changed based on data retrieved from a .getJSON. I have tried including the bulk of the function calcRoute() under a .done function, but I am receiving an error in property waypoints in the javascript console. I am at a loss as what to do, because when I don't include the bulk of the function under the .done, the array remains blank (asynchronous call with the .getJSON. Here is the code to give you a better idea:
function calcRoute() {
var start = document.getElementById('start').value;
var end = document.getElementById('end').value;
var waypts = [];
var data = $.getJSON("/westcoast_map.php", {
westcoast_id: $('.opener').data('westcoast_id')
}, function(json) {
return json[1];
});
data.done(function(theData) {
waypts = theData[1];
console.log(waypts); //this spits out the array in the proper format, i.e. {location:city, state, stopover:true},...etc...
var request = {
origin: start,
destination: end,
waypoints: waypts,
optimizeWaypoints: true,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById('directions_panel');
summaryPanel.innerHTML = '';
// For each route, display summary information.
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
summaryPanel.innerHTML += '<b>Route Segment: ' + routeSegment + '</b><br>';
summaryPanel.innerHTML += route.legs[i].start_address + ' to ';
summaryPanel.innerHTML += route.legs[i].end_address + '<br>';
summaryPanel.innerHTML += route.legs[i].distance.text + '<br><br>';
}
}
});
});
}
i'm still not sure what you problem is, because the code pared you show, should at least work from the logical part. but there are parts where it is not clear what you try to achive:
var data = $.getJSON("/westcoast_map.php", {
westcoast_id: $('.opener').data('westcoast_id')
}, function(json) {
return json[1];
});
if you expect here that data will become json[1], then your assumption is wrong.
$.getJSON returns always jQuery XHR. the callback function will be called later when the browser received the data.
Here a little example to understand how async works:
the callback functions 1 and 2 are called when the client gets the response for the request, but not before the original script was completely executed, so doSomethingElse() will be always called before the callback function 1 and 2 are executed.
the order in which callback function 1 and 2 are executed depends on which response arrives first.
var test = [];
preparesSomeStuff();
$.getJSON("someurl1",{},function() {
//Callback Function 1
});
doSomething();
$.getJSON("someurl2",{},function() {
//Callback Function 2
});
doSomethingElse();
//<<END OF SCRIPT>>
if you don't want to have your whole code inside of the callback function (e.g. because of readability) you could do it the following way:
function calcRoute() {
var start = document.getElementById('start').value;
var end = document.getElementById('end').value;
var waypts = [];
$.getJSON("/westcoast_map.php", {
westcoast_id: $('.opener').data('westcoast_id')
}, function(theData) {
calcualteRoute(theData[1], start, end);
});
//if you place code here it will be executed before displayResult will be called because getJSON is async
}
function calcualteRoute(waypts, start, end) {
console.log(waypts); //this spits out the array in the proper format, i.e. {location:city, state, stopover:true},...etc...
var request = {
origin: start,
destination: end,
waypoints: waypts,
optimizeWaypoints: true,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
displayResult(response,status);
}
});
//if you place some code here it will be executed BEFORE displayResult will be called, because
//directionsService.route is async
}
function displayResult(response, status) {
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById('directions_panel');
summaryPanel.innerHTML = '';
// For each route, display summary information.
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
summaryPanel.innerHTML += '<b>Route Segment: ' + routeSegment + '</b><br>';
summaryPanel.innerHTML += route.legs[i].start_address + ' to ';
summaryPanel.innerHTML += route.legs[i].end_address + '<br>';
summaryPanel.innerHTML += route.legs[i].distance.text + '<br><br>';
}
}