I'm having some trouble with function callbacks, specifically with the HTML5 geolocation API. I'm a little new to Js as well. The function:
function getInfo()
{
navigator.geolocation.getCurrentPosition(getPos);
}
takes a callback parameter of a function that gets the position passed into it.
function getPos(position)
{
PositionObj.lat = position.coords.latitude;
PositionObj.lon = position.coords.longitude;
}
I created a "location" object to store the coordinates, and I'm having trouble returning them.
var PositionObj =
{
lat:null,
lon:null,
returnLat: function()
{
return this.lat;
},
returnLon: function()
{
return this.lon;
}
};
function printStuff()
{
getInfo();
console.log(PositionObj.returnLat());
}
EDIT: Syntax mistakes fixed. When I call printStuff(), I still get "null."
To pass the callback, use just getPos instead of calling the function right away.
Use proper syntax to declare properties in PositionObj.
PositionObj = {
lat: null,
lon: null,
}
Your PositionObj literal is incorrect. the initial properties should be set with colons:
{ lat: '', lon: '' }
The callback should tell you that the request to get position information is asynchronous. This means that getInfo() does NOT set the values instantly, and therefore the following line cannot access the "new" values.
Anything that relies on the result of an asynchronous function MUST be within the callback itself.
Related
I have the following code (that I have stripped and simplified):
function myOrder() {
setLocation(); // sets a value for global variable 'location'
// using GET
$.post('http://111.111.111.111/order', {
Item: Chair,
Price: 50,
Location: location;
})
}
The problem is that this function executes the post() code before the setLocation() callback function has finished.
I need the setLocation() function to finish first, so it can assign a global variable for 'location', which is then used as a value in the POST part.
Does anyone know if this can be done?
Thanks
EDIT: To clarify:
setLocation() is a GET request, that gets the current location via GPS and stores it under a global variable name 'location'.
So what I think is happening is that the myOrder() method POSTS before the callback function has finished its GET response.
Use async flow
function setLocation() {
return new Promise((resolve, reject) => {
// I do my stuff
// I finished
resolve();
});
}
function myOrder() {
setLocation().then(() => {
$.post('http://111.111.111.111/order', {
Item: Chair,
Price: 50,
Location: location;
})
})
}
As you did not provide the implementation of setLocation, I'll assume you use $.get or some similar jQuery function ($.ajax, $.getJSON, ...).
In that case, do not use the callback function of $.get, and do not store the response data a global variable, but return $.get(...) because it is a promise:
function setLocation() {
// Return the promise object returned by $.get, $.getJSON, $.ajax, ...
// instead of using the callback function, and don't store the result
// in a global variable
return $.get("myurl").then(function (data) {
// As you did not provide the code, this serves only as example. The
// way you extract the latitude and longitude from the response may be
// different. Adapt as needed:
data = JSON.parse(data);
var lat = data.result[0].lat;
var lng = data.result[0].lng;
// Return value will be the promised value: adapt the format as needed,
// This will become the location variable content in your other function
return { lat, lng };
});
}
function myOrder() {
// Use the `then` method on the jQuery promise, and return
// the result of the `then` method again, so you can chain
// on myOrder() as well...
return setLocation().then(function (location) {
// Return the `$.post` result:
return $.post('http://111.111.111.111/order', {
Item: Chair,
Price: 50,
Location: location;
});
});
}
If the setLocation function is an asynchronous function, you will need to change it to receive a callback function after it completes.
function myOrder() {
setLocation(function() {
$.post('http://111.111.111.111/order', {
Item: Chair,
Price: 50,
Location: location
});
});
}
Hope it helps.
This may be a novice question but I am trying to create a function that returns true. However, this is based on what happens within several other functions inside.
function checkGeo(){
// CHECK FOR GEOLOCATION
if( "geolocation" in navigator ) {
navigator.geolocation.getCurrentPosition( function(position){
sessionStorage.pinlat = position.coords.latitude;
sessionStorage.pinlon = position.coords.longitude;
// position object is set!
});
// position is not defined
if ( position.coords.latitude && position.coords.longitude ){
return true;
}
}
}
This is the order I want things to happen with my geolocation check but I'm a bit surprised that the nested if is tested before the getCurrentPosition method finishes.
Putting this condition within the getCurrentPosition success function and returning true from there does not make checkGeo return true. How do I check if this asyncronous function has ended and therefore check its results in order to return true?
Have your function have a finished variable
function checkGeo(){
var self = this;
this.ready = function () {}
this.result = false;
if("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(function(position) {
sessionStorage.pinlat = position.coords.latitude;
sessionStorage.pinlon = position.coords.longitude;
self.result = (position.coords.latitude && position.coords.longitude);
self.ready.call(self);
});
}
}
Now you can use the function:
var run = new checkGeo();
run.ready = function () {
alert(this.result); //Both work
alert(run.result); //Both work
};
A bit more complicated, but better programming in my opinion.
position in the anonymous function is not the same as position in the if statement after it. Scope in JavaScript (ignoring ES6 let keyword for simplicity) is by function.
Additionally, if getCurrentPosition() is asynchronous, then you can't rely on the anonymous callback function to run before anything else.
If all you want the return true to signify is that you are trying to get geolocation info without any guarantee that you will be successful, use something more like this:
function checkGeo(){
var hasGeolocation = false;
// CHECK FOR GEOLOCATION
if( "geolocation" in navigator ) {
hasGeolocation = true;
navigator.geolocation.getCurrentPosition( function(position){
sessionStorage.pinlat = position.coords.latitude;
sessionStorage.pinlon = position.coords.longitude;
// position object is set! but only inside this function.
});
return hasGeolocation;
}
}
On the other hand, if you are trying to have return true indicate that the geolocation was successfully set, then you need to indicate it some other way than the return value of the synchronous function, because you won't know that it will be set (an error might occur, a user might disallow geolocation for your site etc.) until the asynchronous function invokes the callback.
The geolocation call is asynchronous, so you can't return the result from the function. When the function ends, you don't yet know the result from the asynchronous call. Returning anything from the callback for the asynchronous call won't make that the return value of the function, because the function has already returned.
You can use callback to report the result back. You have to use the code that checks the position in the callback for the asynchronous call:
function checkGeo(callback){
if( "geolocation" in navigator ) {
navigator.geolocation.getCurrentPosition(function(position){
sessionStorage.pinlat = position.coords.latitude;
sessionStorage.pinlon = position.coords.longitude;
callback(position.coords.latitude && position.coords.longitude);
});
} else {
callback(false);
}
}
Usage:
checkGeo(function(exists){
// here you can use the result
if (exists) {
// ...
}
});
I hope you can help. Ive made a function that receives a lnglat point object and returns just the town. I can get it to print the correct town in the console.log but it doesnt return the data back from the function.
I know its going to be a basic error but can someone have a look at the code and let me know please.
Thanks in advance.
function getTownFromPoint(point){
var geocoder ;
var geocoder = new google.maps.Geocoder();
var townset = false;
mylocation = "";
geocoder.geocode({latLng: point}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0]) {
var components=results[0].address_components;
for (var component=0;component<(components.length);component++){
if(components[component].types[0]=="country" & !townset){
mylocation = components[component].long_name;
}
if(components[component].types[0]=="postal_code" & !townset){
mylocation = components[component].long_name;
}
if(components[component].types[0]=="locality"){
mylocation = components[component].long_name;
townset = true;
console.log(mylocation);
}
}
}
}
});
return(mylocation);
}
Geocoder is asynchronous - You are returning the value before you value is set.
It has been answered here:
Waiting for google maps geocoder?
That's because geocode is an ajax call and they are asynchronous. You need to provide a callback function or use a a promise to get the data. Since you're not using jQuery by the looks of your question a callback might be easier:
Here's a simplified version of your code with an example of how the callback function can be used:
// we pass in the callback as a function parameter
function getTownFromPoint(point, callback) {
geocoder.geocode({
latLng: point
}, function (results, status) {
var myLocation = results.myLocation;
// then we call the callback with the myLocation variable
callback(mylocation);
}
);
}
// call the function with the point variable and the function
// we will use for our callback
getTownFromPoint(1.223, function (myLocation) {
console.log(myLocation)
});
The problem you're facing is that you're treating the geocoder.geocode function as immediately completing before you do the return result. What's really happening is that the geocoder.geocode is triggered, then you get an immediate return of result. Because the asynchronous result has most likely not returned, your result is empty. Think of the geocoding result as a push, not a pull. The storeResult function, not shown, is whatever code you need to do to save the information. Because you're combining a result with an error string, you have to handle that in your storeResult function. As an alternative, you can have a status in the result that indicates succcess or failure.
store the result:
storeResult(result);
use this function inside your function. this will solve your problem
I have been trying to place multiple map markers from lat lng coords in an xml file.
this is my code
var stockistName=[];
var stockistAddress=[];
var stockistLat=[];
var stockistLng=[];
function getAddresses(country){
$.get('xml/stockists/'+country+'.xml', function(d){
alert("file Opened");
$(d).find('stockist').each(function(){
$stockist = $(this);
stockistName.push($stockist.attr("name"));
stockistAddress.push($stockist.attr("address"));
stockistLat.push($stockist.attr("lat"));
stockistLng.push($stockist.attr("lng"));
});
});
placeMarkerArray();
}
function placeMarkerArray(){
alert("this is element 0"+stockistName[0]);
for(var i=0; i<stockistName.length;i++){
alert("in the function got addresses");
//alert("inloop"+i+"");
var newLatLng = new google.maps.LatLng(stockistLat[i], stockistLng[i]);
//alert("making marker lat lng:"+newLatLng+"")
if (markerArray[i] != undefined){
markerArray[i].setPosition(newLatLng);
}
else{
markerArray[i] = new google.maps.Marker({
position: newLatLng,
map: map,
icon:'images/icons/MAP/van.png'
});
}
}
}`
I have been reading about callback functions but can't seem to grasp the idea. I always thought that things would be executed in the order you write them. If anyone could give me any direction that would be great.
I think things are usually clearest with an example, so:
// define a function that accepts a function as a parameter and calls
// this function after one second asynchronously.
function executeActionDelayed (action) {
window.setTimeout(function () {
action();
}, 1000);
}
// Now call the above function with an anonymous function as the parameter.
executeActionDelayed(function () {
console.log("Execute action delayed called.");
});
console.log("End, or is it?");
As you can see in this example, the "End, or is it?" was actually logged to the console before "Execute action delayed called.". This is
because window.setTimeout is asynchronous. The function I created executeActionDelayed is using a callback!
This is similar to what you're doing with jQuery's get function:
// This function is being passed in as a parameter and will be
// called only when the response has been received.
$.get('xml/stockists/'+country+'.xml', function(d){
// stuff removed.
});
So what does this mean for you?
You have to call your function that uses the data within the success callback that you pass in:
function getAddresses(country){
$.get('xml/stockists/'+country+'.xml', function(d){
alert("file Opened");
$(d).find('stockist').each(function(){
$stockist = $(this);
stockistName.push($stockist.attr("name"));
stockistAddress.push($stockist.attr("address"));
stockistLat.push($stockist.attr("lat"));
stockistLng.push($stockist.attr("lng"));
});
placeMarkerArray();
});
}
The $.get method needs to download a file which can take some time. This is why you give it a callback function as a second parameter, it will be called later, when the download is finished.
Meanwhile, your JS keeps executing in the order you wrote it. Which is why after the get call, which starts downloading the file, the placeMarkerArray function is executed.
You should just move the placeMarkerArray() call inside the $.get callback.
Also, you should improve your code indentation, it will make things much clearer.
This question already has an answer here:
Saving geocoder results to an array - Closure Trouble
(1 answer)
Closed 9 years ago.
I have a function
function getCustomAddress() {
alert(results[i].formatted_address)
}
alert(results[i].formatted_address) is defined in another function. It clearly means that it is undefined in getCustomAddress, so how do I resolve this issue and alert the values. I have set up a fiddle as well.
http://jsfiddle.net/KEdrq/5/
You could just pass it as a function parameter
function getCustomAddress(result) {
alert(result.formatted_address)
}
so when you call the function you need to supply one parameter:
getCustomAddress(results[i]); for example
You could create a private scope with a function and define all your global variables there:
(function(){
var results = [];
function getCustomerAdress(){
//... call result etс
}
function set result(){
//... set result etc
}
// some code for initialization, setting onload handlers etc
})();
I checked out the jsFiddle, the results are fetched as an ajax request.
You need to store the results in a variable with a global scope and then set a timeout to fetch the result. You can also execute your function before the end of geocoder request and pass it the results variable.
geocoder.geocode(geocoderRequest, function (results, status) {
// execute your function here. getCustomAddress(result)
}
Check the changes I have made.
http://jsfiddle.net/KEdrq/7/
Summary of code changes.
var _results;
function initialize() {
.
.
.
google.maps.event.addListener(marker, 'dragend', function (e) {
getAddress(e.latLng);
setTimeout('getCustomAddress(0);', 500);
})
function getAddress(latLng) {
if (!geocoder) {
geocoder = new google.maps.Geocoder();
}
var geocoderRequest = {
latLng: latLng
}
geocoder.geocode(geocoderRequest, function (results, status) {
_results = results;
.
.
.
function getCustomAddress(i) {
alert(_results[i].formatted_address)
}
You might want to create a for loop to alert all the results instead of passing the result id in the getCustomAddress function.