I have a class object called Location that works with Google in order to geocode a given address.
The geocode request is made trough an AJAX call and handled via a callback that will initiate the class members once the response arrives.
Here is the code:
function Location(address) {
this.geo = new GClientGeocoder();
this.address = address;
this.coord = [];
var geoCallback = function(result) {
this.coord[0] = result.Placemark[0].Point.coordinates[1];
this.coord[1] = result.Placemark[0].Point.coordinates[0];
window.alert("I am in geoCallback() lat: " + this.coord[0] + "; lon: " + this.coord[1]);
}
this.geo.getLocations(this.address, bind(this, geoCallback));
}
Location.prototype.getAddress = function() { return this.address; }
Location.prototype.getLat = function() { return this.coord[0] }
Location.prototype.getLng = function() { return this.coord[1] }
My question is: it's possible to wait the response from Google before exiting the constructor?
I have no control over the AJAX request since it's made trough Google APIs.
I want to be sure that this.coord[] is properly initialized once a Location obj is created.
Thank you!
No, you can't (read: should not) wait. This is why it's called AJAX ("Asynchronous Javascript ...") in the first place. ;)
You could use a callback function yourself (untested code ahead).
function Location(address, readyCallback) {
this.geo = new GClientGeocoder();
this.address = address;
this.coord = [];
this.onready = readyCallback;
this.geo.getLocations(this.address, bind(this, function(result) {
this.coord[0] = result.Placemark[0].Point.coordinates[1];
this.coord[1] = result.Placemark[0].Point.coordinates[0];
if (typeof this.onready == "function") this.onready.apply(this);
}));
}
Location.prototype.getAddress = function() { return this.address; }
Location.prototype.getLat = function() { return this.coord[0] }
Location.prototype.getLng = function() { return this.coord[1] }
// ... later ...
var l = new Location("Googleplex, Mountain View", function() {
alert(this.getLat());
});
Is it possible to wait the response
from Google before exiting the
constructor?
I wouldn't recommend this approach. When you create a JavaScript object, you don't normally expect it to block for hundreds of milliseconds, until Google responds.
In addition, Google will throttle the GClientGeocoder if you attempt to do frequent requests (Source). There is also a cap for the number of requests a client can do in 24 hours. This would be complicated to handle systematically using this approach. You could easily get into a debugging nightmare if you will be having JavaScript objects that fail randomly.
Related
I'm trying to set then grab a session storage variable that is returned from an API I'm using. The API is returning the proper values, and these values are accessible within the function which returns the result and its status, but not outside of the request. The request:
var geom;
var latArray = new Array();
var longArray = new Array();
function getLatLong(placeIdent) {
service = new google.maps.places.PlacesService(document.createElement('div'));
request = {placeId: ""+placeIdent+""};
service.getDetails(request, function(result, status) {
if (status !== google.maps.places.PlacesServiceStatus.OK) {
console.log(status);
} else {
writeLatLongData(result);
while(geom != "") {
lat = geom.lat();
latArray.push(lat);
long = geom.lng();
latArray.push(long);
console.log(geom);
console.log(lat);
console.log(long);
timeout();
}
}
}
)
}
function writeLatLongData(result) {
geom = result.geometry.location;
return geom;
}
I know this function is the source of the problem, but if it's any help I'm attempting to access the stored values like so:
function timeoutOne() {
setTimeout(function(){
getGeometry(placeArray);
}, 1000);
}
timeoutOne();
function getGeometry(placeArray) {
for (var j=0; j < nightsArray.length; j++) {
getLatLong(placeArray[j]);
}
}
I'm pretty sure it's an async issue, but I'm not that familiar with Async/await and everything I've tried just returns an empty value when the promise is resolved (perhaps because the data are not accessible outside of the request function, still).
Does anyone have any suggestions?
What if below condition gets true,
status !== google.maps.places.PlacesServiceStatus.OK
your google places api call is getting failed (may be because you are making too much calls per 10 seconds), if this happens nothing will be returned which in turn making function getLatLong return undefined.
Check if at least once this function returning something.
I have the following code set up as a job in the Parse Cloud Code for my application.
Parse.Cloud.job("requestLocations", function (request, response) {Parse.Cloud.httpRequest({
url: 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=29.7030428,-98.1364808&radius=900&types=restaurant&key=AIzaSyCTg0x68Q6lrCAo6-A37zkxge81jDEKpvo'
}).then(function (httpResponse) {
// Success
response.success("Success");
var parsedData = JSON.parse(httpResponse.text);
var Location = Parse.Object.extend("Locations");
for (var i = 0; i < parsedData.results.length; i++) {
var restaurant = new Location();
var placeId = parsedData.results[i].place_id;
var name = parsedData.results[i].name;
var vicinity = parsedData.results[i].vicinity;
var point = new Parse.GeoPoint({
latitude: parsedData.results[i].geometry.location.lat,
longitude: parsedData.results[i].geometry.location.lng
});
restaurant.set("placeId", placeId);
restaurant.set("name", name);
restaurant.set("vicinity", vicinity);
restaurant.set("location", point);
restaurant.save(null, {
success: function (location) {
console.log("Object ID: " + location.id);
},
error: function (location, error) {
console.log("Failed to create object, with error code: " + error.message);
}
});
}
}, function (httpResponse) {
// Error
response.error('request failed with response code ' + httpResponse)
});});
As you can see, this HTTP request should return a total of 14 places. Unfortunately, it will only return 9 places and it would also seem that which 9 are return can change. I am assuming there is a problem with the way my function is put together. Can anyone help me remedy this issue. I would like to return as many places as I want based on the radius of the HTTP request.
Thank You
The http request is done right, with a promise that's fulfilled when the request is complete. But your then() block tries to create several objects in a loop, not waiting for them all to finish, and failing to call response.success. Fix it like this...
// break it into understandable chunks, too, so, here's a function
// to build a Locations object from the http data
function locationFromResult(result) {
var Location = Parse.Object.extend("Locations");
var restaurant = new Location();
var placeId = result.place_id;
var name = result.name;
var vicinity = result.vicinity;
var point = new Parse.GeoPoint({
latitude: result.geometry.location.lat,
longitude: result.geometry.location.lng
});
restaurant.set("placeId", placeId);
restaurant.set("name", name);
restaurant.set("vicinity", vicinity);
restaurant.set("location", point);
return restaurant;
}
Parse.Cloud.job("requestLocations", function (request, response) {
var url = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=29.7030428,-98.1364808&radius=900&types=restaurant&key=AIzaSyCTg0x68Q6lrCAo6-A37zkxge81jDEKpvo';
Parse.Cloud.httpRequest({url: url}).then(function (httpResponse) {
var parsedData = JSON.parse(httpResponse.text);
var locations = parsedData.results.map(function(result) {
return locationFromResult(result);
});
// this is important, saveAll of the new objects before returning
// this can also be accomplished by saving the objects individually and using Parse.Promise.when()
return Parse.Object.saveAll(locations);
}).then(function(result) {
response.success(JSON.stringify(result));
}, function(error) {
response.error(JSON.stringify(error));
});
});
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I am trying to learn Node.js
I am having trouble creating my own call back on a function. It seems like such a simple thing but I don't quite understand how to do it.
The function is passed an address (example: "1234 will ln, co") which uses google's geolocate json api to return the full address, latitude and longitude in an array.
Here is my code:
//require secure http module
var https = require("https");
//My google API key
var googleApiKey = "my_private_api_key";
//error function
function printError(error) {
console.error(error.message);
}
function locate(address) {
//accept an address as an argument to geolocate
//replace spaces in the address string with + charectors to make string browser compatiable
address = address.split(' ').join('+');
//var geolocate is the url to get our json object from google's geolocate api
var geolocate = "https://maps.googleapis.com/maps/api/geocode/json?key=";
geolocate += googleApiKey + "&address=" + address;
var reqeust = https.get(geolocate, function (response){
//create empty variable to store response stream
var responsestream = "";
response.on('data', function (chunk){
responsestream += chunk;
}); //end response on data
response.on('end', function (){
if (response.statusCode === 200){
try {
var location = JSON.parse(responsestream);
var fullLocation = {
"address" : location.results[0].formatted_address,
"cord" : location.results[0].geometry.location.lat + "," + location.results[0].geometry.location.lng
};
return fullLocation;
} catch(error) {
printError(error);
}
} else {
printError({ message: "There was an error with Google's Geolocate. Please contact system administrator"});
}
}); //end response on end
}); //end https get request
} //end locate function
So when I try to execute my function
var testing = locate("7678 old spec rd");
console.dir(testing);
The console logs undefined because its not waiting for the return from locate (or at least I am guessing this is the problem).
How do i create a call back so when the locate function returns my array, it runs the console.dir on the array it returned.
Thanks! I hope my question makes sense, im self taught so my tech jargon is horrible.
You need to pass in the callback function to your method - so the callback might look something like this
function logResult(fullLocation){
console.log(fullLocation)
}
You would pass this in to your locate method along with the input:
// note: no parentheses, you're passing a reference to the method itself,
// not executing the method
locate("1234 will ln, co",logResult)
You can also do this inline - much like the response object you're already dealing with:
locate("1234 will ln, co",function(fullLocation){
// do something useful here
})
Now for the bit inside your method, instead of trying to return the result you just call the callback with the result:
function locate(address, callback) {
......
response.on('end', function (){
if (response.statusCode === 200){
try {
var location = JSON.parse(responsestream);
var fullLocation = {
"address" : location.results[0].formatted_address,
"cord" : location.results[0].geometry.location.lat + "," + location.results[0].geometry.location.lng
};
callback(fullLocation); // <-- here!!!
} catch(error) {
printError(error);
}
} else {
printError({ message: "There was an error with Google's Geolocate. Please contact system administrator"});
}
}); //end response on end
.....
}
I'm having trouble sending two request in PHP and waiting for both answers. Here is my code :
function getXDomainRequest() {
var xdr = null;
if (window.XDomainRequest) {
xdr = new XDomainRequest();
} else if (window.XMLHttpRequest) {
xdr = new XMLHttpRequest({mozSystem: true});
} else {
alert("Your browser does not support AJAX");
}
return xdr;
}
function sendData() {
var json1= "";
var json2= "";
var xdr = getXDomainRequest();
xdr.onload = function() {
json1 = xdr.responseText;
}
var xdr2 = getXDomainRequest();
xdr2.onload = function() {
json2 = xdr2.responseText;
}
var Id = document.querySelector('#searchField').value;
// Call API
xdr.open("GET", "./dorequest.php?id=" + Id + "&requesttype=player");
xdr.send();
xdr2.open("GET", "./dorequest.php?id=" + Id + "&requesttype=stats");
xdr2.send();
xdr.wait();
// Display results
getHtmlResults(jsonPlayer, jsonStats);
}
As expected here the json1 and json2 are still empty when getHtmlResults is called. I could do it synchronously by calling the xdr2.send() into the xdr.onload and my final function in the xdr2.onload but I want to do it asynchronously to get a better response time.
Thanks !
(any other comment on the code is welcome I'm quite new to php :) )
EDIT :
So I tryied using Ajax and it seems to work :)
var jsonPlayer = "";
var jsonStats = "";
var steamId = document.querySelector('#searchField').value;
$.when(
$.ajax({url: "./dorequest.php?steamid=" + steamId + "&requesttype=playersummary",
success: function(response){ jsonPlayer = response; }}),
$.ajax({url: "./dorequest.php?steamid=" + steamId + "&requesttype=csgostats",
success: function(response){ jsonStats = response; }}) ).done(
function(player, stats) {
getHtmlResults(player, stats);
});
Promises are commonly used as an abstraction to deal with asynchronous processes.
Wrap your AJAX calls in a Promise to do:
var ajax1 = request("stats");
var ajax2 = request("player");
when(ajax1, ajax2).done(function (stats, player) {
console.log(stats, player);
});
Most popular frameworks have a built-in Promise API.
You can send both the calls ASync and have a function in both .onload which checks if the other request has completed. So as soon as one of the onload finds that the other onload is done, you can call the getHtmlResults function.
I am trying to use the Google Maps API to fetch the city name from a zipcode. This is not my strength (I'm more of a PHP person), so I am using sample code I found, with some modifications suggested by a friend.
The problem is, after I call the function my global variable with the city name is still at it's initialized value of null. If, however, I do an alert with this value, the rest of the processing suddenly has the correct value loaded! I tried putting in a time delay to see if Google was just slow in returning the value, but it makes no difference.
Here's the function:
var geocoder = new google.maps.Geocoder();
function getGoogleAddress(zipcode) {
//var gcity = "N/A"; switch to using global var defined above
geocoder.geocode( { 'address': zipcode}, function (result, status) {
for (var component in result[0]['address_components']) {
for (var i in result[0]['address_components'][component]['types']) {
if (result[0]['address_components'][component]['types'][i] == "locality") {
gcity = result[0]['address_components'][component]['short_name'];
break;
}
}
}
});
}
And this is where it's called from...including the alert and the pause:
gcity="";
getGoogleAddress(form.zip.value);
var holdcity = gcity;
var date = new Date();
var curDate = null;
do { curDate = new Date(); }
while(curDate-date < 2000);
alert(gcity);
As I said, the alert returns null, but the rest of the processing has the proper city name in gcity. If I leave out the alert, the rest of the processing fails because gcity is null.
Any advice or suggestions are greatly appreciated. Thanks.
Asynchronous.
The function (result, status) { is only executed when Google's servers have responded. The rest of your getGoogleAddress function doesn't wait for that, but exits, and Javascript continues execution at var holdcity = gcity.
The reason it works after the alert, is that by then, Google will have responded, and the gcity variable will have been executed.
Possible solution:
var geocoder = new google.maps.Geocoder();
function getGoogleAddress(zipcode, successFunction) {
//var gcity = "N/A"; switch to using global var defined above
geocoder.geocode( { 'address': zipcode}, function (result, status) {
for (var component in result[0]['address_components']) {
for (var i in result[0]['address_components'][component]['types']) {
if (result[0]['address_components'][component]['types'][i] == "locality") {
var gcity = result[0]['address_components'][component]['short_name'];
successFunction(gcity);
break;
}
}
}
});
}
And this is where it's called from...including the alert and the pause:
getGoogleAddress(form.zip.value, function (holdcity) {
var date = new Date();
var curDate = null;
do { curDate = new Date(); }
while(curDate-date < 2000);
alert(holdcity);
});