synchronous call weather api and then display the information - javascript

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!

Related

Repeat function failed in Angular

For my Angular Project, I generated a geolocation component and want to repeat a function findMe() to show the current location.
Part of code in the component.ts is given as below.
...
export class GeolocationComponent implements OnInit{
#ViewChild('gmap') gmapElement: any;
map: google.maps.Map;
isTracking = false;
marker: google.maps.Marker;
constructor(public globalvar: GlobalvarService) { }
ngOnInit() {
var mapProp = {
center: new google.maps.LatLng(-27.542211, 153.1226333),
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
this.map = new google.maps.Map(this.gmapElement.nativeElement, mapProp);
setInterval(this.findMe(), 3000);
}
findMe() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
this.showPosition(position);
console.log("find me");
});
} else {
alert("Geolocation is not supported by this browser.");
}
}
showPosition(position) {
this.globalvar.latitude = position.coords.latitude;
this.globalvar.longitude = position.coords.longitude;
let location = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
this.map.panTo(location);
if (!this.marker) {
this.marker = new google.maps.Marker({
position: location,
map: this.map,
title: 'Got you!'
});
}
else {
this.marker.setPosition(location);
}
}
...
In
ngOnInit(),
I use
setInterval(this.findMe(), 3000);
By checking the log, I see the findMe() is only called once, but not be repeated as that I expect.
I also tried changing findMe() ==> findMe
setInterval(this.findMe, 3000);
this time, the log repeatedly appears, however there is always a error:
ERROR TypeError: _this.showPosition is not a function
Could you please help how can I repeatedly calling findMe() and why the error happens?
The correct way to invoke the interval is with the function declaration setInterval(this.findMe, 3000);. As you noted, if you include the () it is only executed once.
One of the issues that comes up with setInterval is that it changes the this context of the executing function. To fix this, you need to force it to be constant.
constructor(public globalvar: GlobalvarService) {
this.findMe = this.findMe.bind(this);
}
Additional info:
Documentation
StackOverflow Question
You can just use an arrow function that preserves this context :
setInterval(() => this.findMe(), 3000);
You can use arrow function syntax to make it work.
ngOnInit() {
setInterval(() => {
this.findMe()
}, 4000);
}
findMe = () => {
console.log('found');
}
Arrow function is referencing this as Component at all times.
Example - https://stackblitz.com/edit/angular-wkv2he
Try
setInterval(() => {
this.findMe()
}, 3000);
But I think than better solution is to use Observable interval.
interval(3000).subscribe(res => {
this.findMe()
})
Or in older versions of Angular :)
Observable.interval(3000).subscribe(res => {
this.findMe()
})

XMLHttpRequest() Inside RequireJS Module

all. I'm working on a browser-based RPG for fun as well as programming education. I recently learned RequireJS—which is amazing—but during the revision of my code to AMD, I ran into an application breaking snag.
In my original implementation, I used an XMLHttpRequest() request to get the player's selected hero from my server via a GET call to a GetHero.php file. Asynchronous calls are great, but I struggling with the timing of the callback function and continuation of the game—i.e. the game continues before the callback, which results in an error.
My questions are:
1) How can I get the game to wait for the XMLHttpRequest() to return a value before moving to the next block of code?
2) When in my RequireJS AMD should I execute this function?
I tried turning off asynchronous in the .open() function, but since RequireJS uses similar calls, I don't think it works as it would in the synchronous execution of my code. I've also omitted the majority of the code to isolate the specific issue and heighten readability.
Thanks in advance to anyone willing to help me understand this!
main.js
require.config({
baseUrl: "assets/js",
deps: ["game"],
paths: {
jquery: "lib/jquery-3.3.1.min",
bootstrap: "lib/bootstrap.min",
game: "game",
hero: "models/hero",
getHero: "models/getHero",
stats: "models/stats",
heroClasses: "models/heroClasses"
},
shim: {
bootstrap: ["jquery"]
}
});
game.js
define(["hero", "bootstrap"], function (h) {
Hero = h;
console.log(Hero);
console.log(Hero.Stats.Get("str"));
console.log(Hero.Stats.GetInfo()); // <==== this fails
});
hero.js
define(["stats"], function (s) {
Stats = s;
return {
Stats: Stats
};
});
stats.js
define(["getHero"], function(myHero) {
var Info = myHero; // returns hero obj "{ name: "Hero", level: 1, etc..}"
var _HeroClasses = heroClasses;
var _GetInfo = function (arr) {
return Info;
};
return {
GetInfo: _GetInfo
};
});
getHero.js
define([], function () {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var myObj = JSON.parse(this.responseText);
return myObj;
}
};
xmlhttp.open("GET", "/game/assets/php/GetHero.php", false);
xmlhttp.send();
});
GetHero.php
<?php
// TODO create MYSQL call to load hero
echo '{ "id": 0, "name": "Ryan", "classId": 0, "level": 50, "exp": 251 }';
?>
Updated Code: (Working, but likely an ugly/poor solution)
-- game.js (Updated)
var a = new Promise(function (resolve, reject) {
document.getElementById("message").innerHTML = "Loading...";
Hero = h;
resolve(true);
});
a.then(function() {
Hero.Stats.GetInfo();
document.getElementById("message").innerHTML = "Ready";
})
.catch(function(err) {
console.error('There was an error 2!', err.statusText);
});
-- getHero.js (Updated)
define([], function () {
return function (method, url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.send();
});
};
});

Method working when data is static and filed when data is getting from promise

I have a scenario like I need to get some data from api request and based on that show images on the screen.
return Class.extend({
loadData: function (callbacks) {
callbacks.onSuccess(
[
{
"id":"1",
"title":"Apple",
"img" : "static/img/fruit/apple.png"
},
{
"id":"2",
"title":"Banana",
"img" : "static/img/fruit/banana.png"
},
{
"id":"3",
"title":"Grapes",
"img" : "static/img/fruit/grapes.png"
},
{
"id":"4",
"title":"Orange",
"img" : "static/img/fruit/orange.png"
},
{
"id":"5",
"title":"Peach",
"img" : "static/img/fruit/peach.png"
},
{
"id":"6",
"title":"Pear",
"img" : "static/img/fruit/pear.png"
}
]
);
}
});
When I use the above code it works perfectly, But I need data from api so I have implemented a method to get data and I have created a promise also, But this one is not working
return Class.extend({
// You will probably want to do something more useful then returning static data
loadData: function (callbacks) {
callbacks.onSuccess(
evtBind().then(function (res) {
console.log("res", res);
return res;
})
);
}
});
function evtBind() {
var device = RuntimeContext.getDevice();
var get = function () {
return new Promise(function (resolve, reject) {
// do a thing, possibly async, then…
device.executeCrossDomainGet('http://localhost:3000/result', {
onSuccess: function (responseObject) {
resolve(responseObject);
},
onError: function (response) {
reject(response);
}
});
});
};
return get();
}
My code looks like the above one. Can someone help me to resolve this .
If you're getting the correct array in res, then why don't you call your onSuccess with the array? At the moment you're passing a promise to onSuccess so the code there would have to be adjusted if you did that.
loadData: function (callbacks) {
evtBind().then(callbacks.onSuccess);
}
Which is short for:
loadData: function (callbacks) {
evtBind().then(res => callbacks.onSuccess(res));
}

Returning asynchronous results from custom panoProvider

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

Phonegap in Android onDeviceReady function

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.

Categories

Resources