This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
I'm having some trouble returning the latLng value for the following function.
function getUserPosition() {
var latLng;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function (position) {
latLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); // #1
}, function () {
latLng = defaultPosition(); // #2
}
);
} else {
latLng = defaultPosition(); // #3
}
return latLng;
}
Assume that defaultPosition() returns a valid set of coordinates.
I've marked three latLng assignments that are part of my latest 'catch-all' approach but still no luck.
I've checked out other related answers but still can't seem to get my head around my particular issue, so sorry for the repost.
Thanks in advance.
You can't do that because of the asynchronous nature of the location API, instead you need to use a callback based approach where the callback function will be invoked once the locaion API returns the value.
function getUserPosition(callback) {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function (position) {
callback(new google.maps.LatLng(position.coords.latitude, position.coords.longitude));
}, function () {
callback(defaultPosition()); // #2
});
} else {
callback(defaultPosition()); // #3
}
}
getUserPosition(function(latLng){
//do all operations that depends on latLng here in the callback function
})
Related
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
Hello I am a bit inexperienced in Javascript and jasmine and am attempting to write some simple tests for a javascript object.
var googleMap = {
geoCode: function(code, fn) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode({
'address': code
}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
fn(results[0].geometry.location);
} else {
alert("ALL IS LOST");
}
})
},
render: function(LatLng) {
var mapOptions = {
zoom: 8,
center: LatLng
}
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var marker = new google.maps.Marker({
map: map,
position: LatLng
});
}
};
-Here is my jasmine script; I am attempting to make sure that the render function is being called whenever we run the geocode function...
describe("Testing of googleMap.js", function() {
it("Test GeoCode", function() {
var input = "Columbus";
spyOn(googleMap, 'render');
googleMap.geoCode(input, googleMap.render);
expect(googleMap.render).toHaveBeenCalled();
});
});
whenever I run this my specRunner returns an error claiming the expect claimed spy render to have been called, I am a bit baffled as to why it wouldn't have been.... is this to do with how I am attempting to set up my spy, or is the javascript object I made impossible to test. I am having trouble finding similar examples to work from.
The method .geocode of the object google.maps.Geocoder is asynchronous.
Therefore at the time of checking if the callback function was executed, you haven't got your response back and this means your function will not be called.
If all you want is to test if the function will be called then you can do something like the following:
describe("Testing of googleMap.js", function() {
it("Test GeoCode", function(done) {
var input = "Columbus";
googleMap.geoCode(input, function() {
// here you know that your callback function was called
done();
});
});
});
Or if you want still use a mock function you can do this (with jasmine 2.0):
describe("Testing of googleMap.js", function() {
it("Test GeoCode", function(done) {
var input = "Columbus";
var callbackSpy = jasmine.createSpy("callback").and.callFake(function() {
done();
});
googleMap.geoCode(input, callbackSpy);
});
});
With the given code blocks you get an timeout error if the callback function doesn't get called:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Here is a demo (with an mocked google geocoder).
This is how you can fix your code. The question is: Is it reasonable to make a test like this?
In unit tests you should test your code separated from other resources (like google geocoder). So imho in this situation it is better to mock the geocoder object and test if the mock was called with the right arguments.
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.
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.