0
var latLon = "40.8,-77.8"; //Lat/lon
var cityCode = ""; //City code
var cityName = "";
var latLongCityCodeURL = ("http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=" + weatherKey + "&q=" + latLon);

//Current Conditions Vars
var ccWeatherText = ""; //Text for weather at location
var ccTemp = 0; //Degrees Farenheit
var ccIcon = 0; //weather icon number https://developer.accuweather.com/weather-icons
var ccURL = "test"; //URL for get
//12 hour forecast Conditions Vars


//5 day forecast conditions Vars

//Get city code
http.get(latLongCityCodeURL, (resp) => {
    var that = this;
    resp.on("data", (chunk) => {
        var result = JSON.parse(chunk);
        var cityCode = result.Key;
        var cityName = result.EnglishName;
        console.log(cityCode + " " + cityName);
        that.cityName = cityName;
        that.cityCode = cityCode;
    });

}).on("error", (err) => {
    console.log("Error: " + err.message);
});


console.log(cityCode + " " + cityName);

So my issue is, I am making an http request using require('http'), what I want to do is parse the data and store it in my global variables so that I can use them for other requests. I have tried using var that=this and I have tried just assigning my global variables to the data. I am not sure how to do it, i just keep getting undefined. I know it has something to do with ASYNC and also something to do with the scope. Please help

4
  • what do you want to achieve? When do you need "that"? Commented May 16, 2018 at 6:15
  • resp.on will happen at some indeterminate time in the future. You don’t know when that is, so you won’t know when you can access the global. This is why we use callbacks and promises. You’ll need to structure your code so it doesn’t depend on an async function setting global variables. Commented May 16, 2018 at 6:16
  • What is your expected output/ Commented May 16, 2018 at 6:22
  • I am expecting to get a city code and name, which I want to assign to my global variables because I am going to make other request calls that need to contain those variables. Commented May 16, 2018 at 6:27

6 Answers 6

2

You can save your result to a variable at various levels of scope.. just remember that most i/o calls in Node.js are asynchronous. Here's an example:

var latLon = "40.8,-77.8"; //Lat/lon
var cityCode = ""; //City code
var cityName = "";
var latLongCityCodeURL = ("http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=" + weatherKey + "&q=" + latLon);

//Current Conditions Vars
var ccWeatherText = ""; //Text for weather at location
var ccTemp = 0; //Degrees Farenheit
var ccIcon = 0; //weather icon number https://developer.accuweather.com/weather-icons
var ccURL = "test"; //URL for get


var savedResult = null;

//Get city code
http.get(latLongCityCodeURL, (resp) => {
    var jsonData = '';
    resp.on("data", (chunk) => {
        jsonData += chunk;
    });
    resp.on("end", () => {
        savedResult = JSON.parse(jsonData);
    });
}).on("error", (err) => {
    console.log("Error: " + err.message);
});


// Display saved result once available.
setTimeout(displaySavedResult, 2000);

function displaySavedResult() {
    if (!savedResult) {
        console.log('Last result is null!');
    } else {
        console.log('Last result: City Code: ' + savedResult.Key + " Name" + savedResult.EnglishName);
        console.log('Last result (all properties): ', JSON.stringify(savedResult, null, 2));
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

thank you this works, now what if results dont come back within 2000ms? will it fail? is there sure way to check that
The timeout seems rather unnecessary. And still won't work on connection more than 2sec
Hi @Jacob, 2000ms is an arbitrary amount to wait. It could be any amount of time, I've only added the setTimout for illustration.. the idea is that if the savedResult variable is populated it is available to other calls, for example a HTTP get request to your service. You could even use a setInterval call to display the last result periodically, e.g. setInterval(displaySavedResult, 10000);
So anything inside that on.("end") will be returned outside the scope of the function? could I set global variables values inside of there for outside use?
You could set global variables there if you wished.
1

You can use Promise to make http request, here is code that may help you

const httpGet = url => {
    return new Promise((resolve, reject) => {
        http.get(url, res => {
            let body = '';
            res.on('data', chunk => body += chunk);
            res.on('end', () => {
                try {
                    body = JSON.parse(body);
                } catch (err) {
                    reject(new Error(err));
                }
                resolve({
                    cityCode: body.Key,
                    cityName: body.EnglishName
                });
            });
        }).on('error', reject);
    });
};


httpGet(latLongCityCodeURL).then(data => {
    console.log(data.cityCode + " " + data.cityName);
}).catch(err => console.log('Got error ', err));

Comments

1
var latLon = "40.8,-77.8"; 
var latLongCityCodeURL = ("http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=" + weatherKey + "&q=" + latLon);

//Current Conditions Vars
var ccWeatherText = ""; //Text for weather at location
var ccTemp = 0; //Degrees Farenheit
var ccIcon = 0; //weather icon number https://developer.accuweather.com/weather-icons
var ccURL = "test"; //URL for get
//12 hour forecast Conditions Vars


//5 day forecast conditions Vars


//Get city code
function getCityCode(latLongCityCodeURL){
 return new Promise((resolve, reject) => {
  http.get(latLongCityCodeURL, (resp) => {
    resp.on("data", (chunk) => {
        var result = JSON.parse(chunk);
        var cityCode = result.Key;
        var cityName = result.EnglishName;
        resolve({cityCode, cityName});
    });

    }).on("error", (err) => {
       reject(err);
      console.log("Error: " + err.message);
   });

 })
}


getCityCode(latLongCityCodeURL)
.then((result) => { 
 console.log(result.cityCode, result.cityName) 
}).catch((err) => console.log(err))

Another way is to use async-await API interface which is supported in node 8.

async function getCityCode(latLongCityCodeURL){
 const result = await http.get(latLongCityCodeURL, (resp) => {
        resp.on("data", (chunk) => {
            var result = JSON.parse(chunk);
            var cityCode = result.Key;
            var cityName = result.EnglishName;
            return {cityCode, cityName};
        });

        }).on("error", (err) => {
           return err;
          console.log("Error: " + err.message);
       });

  return result;
}

getCityCode(latLongCityCodeURL)
.then((res) => { 

console.log(res.cityCode, res.cityName)

})

Comments

0

Your code block runs in a synchronized way and console.log part hits just after http.get call. The thing is http.get is an async function and its callback part will be called future ticks of NodeJS when your response has arrived.

1 Comment

could you elaborate? I have tried to use a function that calls the http.get with a callback but I think I am doing it wrong.
0
var http = require('http');
var latLon = "40.8,-77.8"; //Lat/lon
var cityCode = ""; //City code
var cityName = "";
var latLongCityCodeURL = ("http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=" + "fmKWSgaG5EAA0diCP2lSREEOYG6PC5q9" + "&q=" + latLon);
var that;
//Current Conditions Vars
var ccWeatherText = ""; //Text for weather at location
var ccTemp = 0; //Degrees Farenheit
var ccIcon = 0; //weather icon number https://developer.accuweather.com/weather-icons
var ccURL = "test"; //URL for get
//12 hour forecast Conditions Vars


//5 day forecast conditions Vars

//Get city code
getWeather = url => {
    return new Promise((resolve, reject) => {
        http.get(url, res => {
            let body = '';
            res.on('data', chunk => resolve(JSON.parse(chunk)));
        }).on('error', reject);
    });
};


getWeather(latLongCityCodeURL).then( weather => {
    console.log(weather.Key + " " + weather.EnglishName);
})

Comments

0

You must wait for the response, then send the other requests or log into. The scope isn't really the issue here, it's the order of operations that you're missing. You define a variable, then wait for a HTTP request, meanwhile you're immediately logging a value, then the request finishes (or times out or other error), and you can log again the value - at which point you see data.

In other words, to fix the issue, other requests must be made from within a function of resp.on("end", which says when you've finished getting all data chunks

And by "within", the code can still be in a separate method, outside those brackets, but you must call that particular function from within that response body.

You should pass such asynchronous returned data through parameter variables, not update some external, global state in most cases

2 Comments

Ok, so essentially I should nest all my requests inside of each other? I could do that, but what about the end result of my requests, which i then need for later use with a post call from my app
The end result of one request is either another request, or updating the state of some UI or data object. Promises can make the data transfer somewhat easier, as the other answer shows

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.