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
}
});
}
}
Related
I am having difficulty with the way google calls its maps api. I have the following calling initMap
<script defer
src="https://maps.googleapis.com/maps/api/js?key=API_KEY_REMOVED&callback=initMap">
</script>
but inside initMap, the following condition if(getPosition() !== false) { never evaluates to true because init map is done before getPosition() has set its object values.
function initMap() {
// set new map, assign default properties
map = new google.maps.Map(document.getElementById('map'), {
center: { lat, lng }, zoom: 14
});
// check if the requested data is usable (lat, lng === numbers) before trying to use it
if(getPosition() !== false) {
map.setCenter( getPosition() ); // set latest position as the map center
addMarker();
console.log("InitMap ran here");
}
}
How can I make it so initMap waits until getPosition() has had a chance to wait for other functions to do their thing? Here is my complete script so it makes more sense.
<script>
console.log(formatTime(Date()));
// https://developers.google.com/maps/documentation/javascript/geolocation
var map; var marker;
var lat = 65.025984; var lng = 25.470794; // default map location in case no position response is available
var res_data; var res_longitude; var res_latitude; var res_speed; var res_time; // res = response (data from the ajax call)
var xhr = new XMLHttpRequest();
function getPosition() {
pos = {
lat: res_latitude,
lng: res_longitude,
};
return ( isNaN(pos.lat) || isNaN(pos.lng) ) ? false : pos; // return pos only if lat and lng values are numbers
}
function initMap() {
// set new map, assign default properties
map = new google.maps.Map(document.getElementById('map'), {
center: { lat, lng }, zoom: 14
});
// check if the requested data is usable (lat, lng === numbers) before trying to use it
if(getPosition() !== false) {
map.setCenter( getPosition() ); // set latest position as the map center
addMarker();
console.log("InitMap ran here");
}
}
// place marker on the map
function addMarker() {
//console.log("Add Marker ran");
//https://developers.google.com/maps/documentation/javascript/markers
if(marker){ marker.setMap(null); } // remove visibility of current marker
marker = new google.maps.Marker({
position: getPosition(),
map: map,
title: formatTime(res_time),
});
marker.setMap(map); // set the marker
}
function getData() {
xhr.addEventListener("load", reqListener);
xhr.open("GET", "http://example.com/data.txt");
xhr.send();
}
function reqListener() {
// res_data = long, lat, accuracy, speed, time
//console.log("reqListener: " + xhr.responseText);
res_data = '[' + xhr.responseText + ']';
res_data = JSON.parse(res_data);
res_latitude = res_data[0]; res_longitude = res_data[1]; res_accuracy = res_data[2]; res_speed = res_data[3]; res_time = res_data[4];
var formatted_time = formatTime(res_time);
document.getElementById('info').innerHTML = '<span class="info">Lat: ' + res_latitude + '</span><span class="info">Long: ' + res_longitude + '</span><span class="info">Accuracy: ' + res_accuracy + '</span><span class="info">Speed: ' + res_speed + '</span><span class="info">' + formatted_time + '</span>';
addMarker();
}
function formatTime(time) {
var t = new Date(time);
var hours, mins, secs;
if(t.getHours() < 10) { hours = "0" + t.getHours(); } else { hours = t.getHours(); }
if(t.getMinutes() < 10) { mins = "0" + t.getMinutes(); } else { mins = t.getMinutes(); }
if(t.getSeconds() < 10) { secs = "0" + t.getSeconds(); } else { secs = t.getSeconds(); }
var hms = hours +':'+ mins +':'+ secs;
return 'Updated: ' + hms;
}
function init() {
getData();
setInterval(getData, 5000);
}
init();
</script>
<script defer
src="https://maps.googleapis.com/maps/api/js?key=API_KEY_REMOVED&callback=initMap">
</script>
Get rid of the callback=initMap from where you load in the Maps API.
Instead make a call to initMap only from where you are then certain everything is loaded. e.g. at the end of reqListener.
function reqListener() {
res_data = '[' + xhr.responseText + ']';
res_data = JSON.parse(res_data);
res_latitude = res_data[0]; res_longitude = res_data[1]; res_accuracy = res_data[2]; res_speed = res_data[3]; res_time = res_data[4];
var formatted_time = formatTime(res_time);
document.getElementById('info').innerHTML = '<span class="info">Lat: ' + res_latitude + '</span><span class="info">Long: ' + res_longitude + '</span><span class="info">Accuracy: ' + res_accuracy + '</span><span class="info">Speed: ' + res_speed + '</span><span class="info">' + formatted_time + '</span>';
initMap();
addMarker();
}
If you're calling reqListener at repeated intervals and don't want to recreate your map, add some logic to the top of initMap like:
if (map !== null) {
return;
}
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)
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 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>';
}
}
I have set an onclick with a variable on a link that calls the calcRoute function for Google maps, and everytime I click the link it says the following Uncaught ReferenceError: NE461UL is not defined The parameter is a postcode by the way.
I have been trying for a while and I can't figure out why it is showing an error.
I have a jquery file with the following line
var distLink = "<a onclick=\"calcRoute(" + postcode + "); return false;\" datalat=" + lat + " datalng=" + lng + " id=\"get-directions\" class=\"directions" + letter +"\">Directions<\/a>";
The calcRoute is in my header
function calcRoute(postcode) {
console.log(marker);
var start = document.getElementById("address").value;
var end = document.getElementById("get-directions").name;
$('get-directions').click(function(){
console.log('click!');
});
var request = {
origin:start,
destination:end,
travelMode: google.maps.TravelMode.DRIVING
};
console.log(JSON.stringify(request));
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
}
postcode must be passed as a string (extra pair of quotation marks around it):
distLink = "<a onclick=\"calcRoute('" + postcode + "'); return false;\" datalat=" + lat + " datalng=" + lng + " id=\"get-directions\" class=\"directions" + letter +"\">Directions<\/a>";