I know there are many, many answers to this question on SO, but I haven't been able to find one that applies in this case. I'm making an asynchronous call to a Google service and need to return the result:
function getCustomPanorama(pano,zoom,tileX,tileY) {
client.getPanoramaById(pano, function(result, status) {
return {
location: result.location,
links: result.links,
copyright: result.copyright+' BUT GREY',
tiles: {
tileSize: result.tiles.tileSize,
worldSize: result.tiles.worldSize,
centerHeading: result.tiles.centerHeading,
getTileUrl: getCustomPanoramaTileUrl
}
};
});
}
I understand that the above is wrong and will not return, and think I need to use callbacks, but I don't understand where. Note that I can't change what is passed to getCustomPanorama. All help gratefully received.
UPDATE: Full code:
var panorama;
var client;
$(document).ready(function() {
var panoramaOptions = {
position: new google.maps.LatLng(51.52241608253253, -0.10488510131835938),
panoProvider: getCustomPanorama
};
client = new google.maps.StreetViewService();
panorama = new google.maps.StreetViewPanorama(document.getElementById("pano"), panoramaOptions);
});
function getCustomPanorama(pano,zoom,tileX,tileY) {
client.getPanoramaById(pano, function(result, status) {
return {
location: result.location,
links: result.links,
copyright: result.copyright+' BUT GREY',
tiles: {
tileSize: result.tiles.tileSize,
worldSize: result.tiles.worldSize,
centerHeading: result.tiles.centerHeading,
getTileUrl: getCustomPanoramaTileUrl
}
};
});
}
UPDATE 2:
Suggestions are definitely that I'm trying to do something impossible, so trying another approach involving pre-caching the getPanoramaByID() responses.
Change getCustomPanorama to take an extra parameter for the callback and pass in a function that does what you needed to do with the result:
function getCustomPanorama(pano,zoom,tileX,tileY,callback) {
client.getPanoramaById(pano, function(result, status) {
var data = {
location: result.location,
links: result.links,
copyright: result.copyright+' BUT GREY',
tiles: {
tileSize: result.tiles.tileSize,
worldSize: result.tiles.worldSize,
centerHeading: result.tiles.centerHeading,
getTileUrl: getCustomPanoramaTileUrl
}
};
callback(data); // call the function and pass in the data you would have returned
});
}
getCustomPanorama(pano,zoom,tileX,tileY,function(data) {
// do something with the results of the asynchronous call here
});
panoProvider is not supposed to be called asynchronously. That means you have to have all necessary information for creating custom StreetViewPanoramas pre-populated.
But if you really need to call client.getPanoramaById inside of panoProvider then there is a very dirty trick:
function getCustomPanorama(pano,zoom,tileX,tileY) {
var resultFromAsyncCall;
client.getPanoramaById(pano, function(result, status) {
resultFromAsyncCall = {
...
copyright: result.copyright+' BUT GREY',
tiles: {
...
getTileUrl: getCustomPanoramaTileUrl
}
};
});
while (!resultFromAsyncCall) {
//wait for result
}
return resultFromAsyncCall;
}
BUT, I discourage you from using this solution. Better try to re-think logic of your application.
Related question: Call An Asynchronous Javascript Function Synchronously
Related
Purpose:
onClick event trigger synchronous call to getWeather() then call display()
var position={} // object containing latitude and longitude
var getWeather = function(pos) {
$.getJSON('https://api.openweathermap.org/data/2.5/weather', {
lat: pos.lat,
lon: pos.lng,
appid: "*****appid***"
}, showWeather, 'jsonp');
return new Promise(function (resolve, reject) {
resolve(weather_data)
});
};
var showWeather = function(data) {
return weather_data;
};
function display(weather_info){
console.log(weather_info);
}
These are the above two functions that I want to run synchronously using promise and in the respective order;
Problem:
When I used it in click function it does not not return the weather_data.
$("html").click(function() {
getWeather(position)
.display(getWeather);
});
What am I doing wrong?
showWeather callback function from $.getJSON is creating this trouble. I don't know where to return the promise();
I solved it myself
JsFiddle of the correction
var getWeather = function(pos) {
return new Promise(function (resolve, reject) {
var data_w={};
$.getJSON('https://api.openweathermap.org/data/2.5/weather', {
lat: pos.lat,
lon: pos.lng,
appid: "*****appid****"
}, function(data) { resolve(data)}, 'jsonp');
});
};
This above lines speaks for themselves. I have made the whole API call a promise. That's how I solved it.
If someone finds better way, please respond!
I have an alfresco webscript who return a json response.
I have a js function getWorkflowRepositoryContent() who call this webscript and get the data retuned in the response.
I store the response.json in an array list.
All works fine for me, but when i call getWorkflowRepositoryContent() from another js function, it returned an empty array when it must return an array containing the data received from webscript response.
There is the function where i return the data received from the webscript.
Can you tell me what i made a mistake, or tell me how to properly return the data from that function.
function getWorkflowRepositoryContent(){
var list=[];
var workflowFilesNameAndNodeRef;
var test=function getWorkflowFilesList(response)
{
workflowFilesNameAndNodeRef=response.json.nodes;
$.each(response.json.nodes,function(index,value){
list.push(value.name);
});
}
Alfresco.util.Ajax.request(
{
method:Alfresco.util.Ajax.GET,
url: Alfresco.constants.PROXY_URI + "/ALFRESCO-DIRECTORY",
successCallback:
{
fn:test,
scope:this
},
failureCallback:
{
fn: function(response)
{
Alfresco.util.PopupManager.displayMessage({text:"Failure"});
},
scope: this
}
});
console.log(list.length);
return list;
}
Your getWorkflowRepositoryContent is getting asynchronous data but returning synchronously so your example won't work.
An easy way would be to simple call your function with a callback argument.
function getWorkflowRepositoryContent(cb){ // pass a callback as an argument
var list=[];
var workflowFilesNameAndNodeRef;
var test=function getWorkflowFilesList(response)
{
workflowFilesNameAndNodeRef=response.json.nodes;
console.log(response.json.nodes);
$.each(response.json.nodes,function(index,value){
list.push(value.name);
});
$.each(list,function(index, fileName){
$('<option/>').val(fileName).html(fileName).appendTo('#saveButton');
$('<option/>').val(fileName).html(fileName).appendTo('#loadButton');
});
cb(list); // call the callback once the work is done
}
Alfresco.util.Ajax.request(
{
method:Alfresco.util.Ajax.GET,
url: Alfresco.constants.PROXY_URI + "/ALFRESCO-DIRECTORY",
successCallback:
{
fn:test,
scope:this
},
failureCallback:
{
fn: function(response)
{
Alfresco.util.PopupManager.displayMessage({text:"Failure To get StarXpert Workflow content"});
},
scope: this
}
});
}
getWorkflowRepositoryContent( function(list) {
console.log(list);
});
You could also use promises but it might be a little harder if you're not familiar with them.
I have some code that independently works, but when I put it together I get "Undefined" error at "this"
It surly have something to to with my lack of understanding JS this, that.
I get it to work in Ionic 1, but not in Ionic 2.
I have two events, "MAP_READY" from Maps and "key_entered" from Geofire.
I would we very thankful for guidance. Here is the essential parts of the code:
platform.ready().then(() => {
this.LOADMAP();
});
//---------Loading map ----------
LOADMAP(){
Geolocation.getCurrentPosition().then((position) => {
this.map = new GoogleMap('map', {
'backgroundColor': 'white',
'controls':....
...
...
}
this.map.on(GoogleMapsEvent.MAP_READY).subscribe(() => {
this.GETMARKERS();
},
(err) => {
console.log(err);
});
});
}
//-----Get markers from Geofire/Firebase
GETMARKERS(){
...
...
var geoQuery = geoFire.query({
center: [lat,lon],
radius: 3000
});
...
var onKeyEnteredRegistration=GeoQuery.on("key_entered",
function(key,location) {
this.ADDMARKER(location)
});
}
}
//-------Adding marker to map
ADDMARKER(location){
let markerOptions: GoogleMapsMarkerOptions =
{position: location,title:'Some title' };
this.map.addMarker(markerOptions) <-------------------- This gives error
.then((marker: GoogleMapsMarker) => {
marker.showInfoWindow();
});
}
the error may show here
this.map.addMarker(markerOptions)
but actual error is at
var onKeyEnteredRegistration=GeoQuery.on("key_entered",
function(key,location) { <---------- HERE
this.ADDMARKER(location)
});
}
}
Change your code to
var onKeyEnteredRegistration=GeoQuery.on("key_entered",
(key,location) =>{
this.ADDMARKER(location)
});
}
}
'function' will change the scope, that's why 'this' wont work inside the block.
Hope this helps! :)
I'm building a google maps app with React.js.
My instinct is to create separate file with an ES6 class to handle search queries - within the class there will be a function that returns the search result. I intend to call that function from within a React Component.
See the component below for reference.
Should I move the code inside the findRoutes() and drawBoxes() methods to separate files? This is my first react app - want to learn how to best organize the code. Any tips are hugely appreciated.
var MapControl = React.createClass({
getInitialState: function(){
return {
originId: '',
destinationId: '',
radius: 1,
search: '',
map: {},
travelMode: google.maps.TravelMode.DRIVING
}
},
handleFormSubmit: function(input){
// Form Input
// Call findRoutes() once setState is complete.
this.setState({
originId: input.originId,
destinationId: input.destinationId,
radius: input.radius,
search: input.search
}, this.findRoutes);
},
handleMapRender: function(map){
// Intialized Google Map
this.setState({map: map});
directionsService = new google.maps.DirectionsService();
directionsDisplay = new google.maps.DirectionsRenderer();
routeBoxer = new RouteBoxer();
directionsDisplay.setMap(map);
placesService = new google.maps.places.PlacesService(map);
},
findRoutes: function(){
var me = this;
if (!this.state.originId || !this.state.destinationId) {
alert("findRoutes!");
return;
}
directionsService.route({
origin: {'placeId': this.state.originId},
destination: {'placeId': this.state.destinationId},
travelMode: this.state.travelMode
}, _.bind(function(response, status){
if (status === google.maps.DirectionsStatus.OK) {
// me.response = response;
directionsDisplay.setDirections(response);
var path = response.routes[0].overview_path;
this.setState({
routes: response,
boxes: routeBoxer.box(path, this.state.radius)
},this.drawBoxes);
} else {
window.alert('Directions config failed due to ' + status);
}
}, this));
},
drawBoxes: function(){
var boxpolys = new Array(this.state.boxes.length);
for (var i = 0; i < this.state.boxes.length; i++) {
boxpolys[i] = new google.maps.Rectangle({
bounds: this.state.boxes[i],
fillOpacity: 0,
strokeOpacity: 1.0,
strokeColor: '#000000',
strokeWeight: 1,
map: this.state.map
});
}
},
render: function() {
return (
<div className="MapControl">
<h1>Search</h1>
<MapForm
onFormSubmit={this.handleFormSubmit}
map={this.state.map}/>
<GMap
setMapState={this.handleMapRender}
originId= {this.state.originId}
destinationId= {this.state.destinationId}
radius= {this.state.radius}
search= {this.state.search}/>
</div>
);
}
});
Your instinct is correct, in general it's good practice to keep your view code as minimal as possible, and to move anything that is not explicitly related to rendering your view into another class.
React.js itself is not opinionated as to how you handle the rest of your application needs but a few libraries have emerged as the leading solutions for handling events, routing, services, data, etc.
You should review the Flux pattern for an overview of how you might manage the rest of your applications needs for React apps.
https://facebook.github.io/flux/docs/overview.html
I've moved on from Flux to using Redux recently
http://redux.js.org/ (which has emerged as the leading 'Fluxish' state management lib)
If you want to just get something up and running quickly and aren't trying to dive into all that just yet, I'd move the findRoutes method to an external service that you import in... drawBoxes seems fine to keep in here as it is directly related to rendering map elements in the view
I am using phonegap in android development. I wrote that PoC, however I can not figure out why it does not change the latitude of profile variable. Actually
alert(profile.latitude);
runs before the
geoCode.setLocation();
here is my code;
document.addEventListener("deviceready", onDeviceReady, false);
var profile = {
name: "",
id: "red",
latitude: "xx",
longtitude: "",
setCoordinates: function (latit, longt) {
this.latitude = latit;
this.longtitude = longt;
}
};
var geoCode = {
onSuccess: function (position) {
profile.latitude = position.coords.latitude;
},
onError: function (error) {
},
setLocation : function () {
navigator.geolocation.getCurrentPosition(this.onSuccess, this.onError);
}
};
// Wait for PhoneGap to load
//
function onDeviceReady() {
geoCode.setLocation();
//alert("2");
alert(profile.latitude);
};
thanks in advance
navigator.geolocation.getCurrentPosition is an asynchronous function. You need to do something like :
var geoCode = {
setLocation : function (callback) {
onSuccess: function (position) {
callback(position.coords.latitude);
},
onError: function (error) {
},
navigator.geolocation.getCurrentPosition(onSuccess, onError);
}
};
// Wait for PhoneGap to load
//
function onDeviceReady() {
geoCode.setLocation(function(latitude) {
alert(latitude);
});
};
Quite simply it is because the call to navigator.geolocation.getCurrentPosition() is an asynchronous call. Thereby the execution of the program continues and you see the alert. Sometime after the alert is show the onSuccess call of your geoCode class is called updating the profile.latitude value.