Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Node js run the function synchronously

I am building a weather app but here in the code. The first three lines requests the and sends response. But before the response sent the line last runs and prints "undefined" in the console

//sending the request
const APIMannager = require("./locationToWeather.js");
const apiMannager = new APIMannager();
const data = apiMannager.getLocation("pokhara");
console.log(data); //prints undefined
//how to run the program one line after another

2 Answers

Can you post your code of the APIManager class? You may not want to run it synchronously as it will be blocking the execution thread. However, you can design your getLocation method in a way to accept a callback or make use of Promises. If you can post the APIManager class code, I can maybe give you some suggestions.

Ok, but its code is long

What it does it take location string and get latitude and longitude via google api and again return weather info of that location via DarkSky api

const https = require("https");
class ApiMannager {

    getLocation(location) {
        //API keys
        console.log("It was called and location provided is " + location);
        const googleAPI = "AIzaSyDeK7vifEDzmEl9ewyDg7iH2o8EleWtevM";
        // eg: https://maps.googleapis.com/maps/api/geocode/json?address=pokhara&keyAIzaSyDeK7vifEDzmEl9ewyDg7iH2o8EleWtevM
        let url = "https://maps.googleapis.com/maps/api/geocode/json?address=" +
            location + "&key=" +
            googleAPI;

        const request = https.get(url, (response) => {
            let data = "";
            console.log("Atleast response came");
            response.on('data', (d) => {
                data += d.toString();
                console.log("data is comming");
            });

            response.on('end', (d) => {
                const locationInfo = JSON.parse(data);
                let loc = locationInfo.results[0].geometry.location;
                let lat = loc.lat;
                let lng = loc.lng;
                this.getWeatherInfo(location, lat, lng);
            });
        });
    }

    getWeatherInfo(location, latitude, longitude) {
        const darkskyAPI = "9a03992fa5a35806ef16e39dbe970fdc";
        const DarkSkyUrl = "https://api.darksky.net/forecast/" +
            darkskyAPI + "/" +
            latitude + "," +
            longitude;

        const error = https.get(DarkSkyUrl, (response) => {
            let data = "";
            response.on('data', (d) => {
                data += d.toString();
            });

            response.on("end", (d) => {
                const weatherInfo = JSON.parse(data);
                const currentInfo = weatherInfo.currently

                const reqData = {
                    location: location,
                    temperature: currentInfo.temperature,
                    humidity: currentInfo.humidity,
                    pressure: currentInfo.pressure,
                    windSpeed: currentInfo.windSpeed
                }
                console.log(reqData);
                return reqData;
            });
        });
    }
}

module.exports = ApiMannager;
          ```

Thanks. There are 2 ways you can achieve what you are looking for. It was a bit trickier as you were using two different functions, but I think I got what you are looking for.

  1. Utilize a callback function in your getLocation method. So the signature would look something like: javascript getLocation("Austin", (data)=>{})
  2. Utilize the node event emitter, in your APIManager class. Your APIManager will inherit from EventEmitter.

I posted both solutions below and put comments on what I changed:

  1. The callback way
//FILE: app.js
//sending the request
const APIMannager = require("./locationToWeather.js");
const apiMannager = new APIMannager();
//CHANGE: Here I am passing the second parameter the callback which will contain the wheather data
apiMannager.getLocation("Austin", (data)=>{
    console.log(data);
});

//FILE: locationToWeahter.js
const https = require("https");
class ApiMannager {
    //ChANGE: As you can see I am passing an additional parameter which will be the callback function
    //Basically what I am doing is execute getLocation, then execute getWeatherInfo and once that is done, I go back up the chain with the callback
    getLocation(location, callback) {
        //API keys
        console.log("It was called and location provided is " + location);
        const googleAPI = "AIzaSyDeK7vifEDzmEl9ewyDg7iH2o8EleWtevM";
        // eg: https://maps.googleapis.com/maps/api/geocode/json?address=pokhara&keyAIzaSyDeK7vifEDzmEl9ewyDg7iH2o8EleWtevM
        let url = "https://maps.googleapis.com/maps/api/geocode/json?address=" +
            location + "&key=" +
            googleAPI;

        const request = https.get(url, (response) => {
            let data = "";
            console.log("Atleast response came");
            response.on('data', (d) => {
                data += d.toString();
                //console.log("data is comming");
            });

            response.on('end', (d) => {
                const locationInfo = JSON.parse(data);
                let loc = locationInfo.results[0].geometry.location;
                let lat = loc.lat;
                let lng = loc.lng;
                return this.getWeatherInfo(location, lat, lng, callback);
            });
        });
    }

    getWeatherInfo(location, latitude, longitude, callback) {
        const darkskyAPI = "9a03992fa5a35806ef16e39dbe970fdc";
        const DarkSkyUrl = "https://api.darksky.net/forecast/" +
            darkskyAPI + "/" +
            latitude + "," +
            longitude;

        const error = https.get(DarkSkyUrl, (response) => {
            let data = "";
            response.on('data', (d) => {
                data += d.toString();
            });

            response.on("end", (d) => {
                const weatherInfo = JSON.parse(data);
                const currentInfo = weatherInfo.currently

                const reqData = {
                    location: location,
                    temperature: currentInfo.temperature,
                    humidity: currentInfo.humidity,
                    pressure: currentInfo.pressure,
                    windSpeed: currentInfo.windSpeed
                }
                //console.log(reqData);
                return callback(reqData);
            });
        });
    }
}

module.exports = ApiMannager;
  1. The EventEmitter way
//FILE: app.js
//sending the request
const APIMannager = require("./weatherEmitter.js");
const apiMannager = new APIMannager();
//CHANGE: Here I wait for the .on('wheater') event to occur which contains all the data I need
apiMannager.on('weather', (data)=>{
    console.log('inside on weather');
    console.log(data);
});

//FILE: weatherEmitter.js
const https = require("https");
const EventEmitter = require('events').EventEmitter;

//CHANGE: I am inheriting from EventEmitter so I can emit events with .emit() and listen to them with .on()
class ApiMannager extends EventEmitter {


    getLocation(location) {
        //API keys

        console.log("It was called and location provided is " + location);
        const googleAPI = "AIzaSyDeK7vifEDzmEl9ewyDg7iH2o8EleWtevM";
        // eg: https://maps.googleapis.com/maps/api/geocode/json?address=pokhara&keyAIzaSyDeK7vifEDzmEl9ewyDg7iH2o8EleWtevM
        let url = "https://maps.googleapis.com/maps/api/geocode/json?address=" +
            location + "&key=" +
            googleAPI;

        const request = https.get(url, (response) => {
            let data = "";
            console.log("Atleast response came");
            response.on('data', (d) => {
                data += d.toString();
                //console.log("data is comming");
            });

            response.on('end', (d) => {
                const locationInfo = JSON.parse(data);
                let loc = locationInfo.results[0].geometry.location;
                let lat = loc.lat;
                let lng = loc.lng;
                this.getWeatherInfo(location, lat, lng);
            });
        });
    }

    getWeatherInfo(location, latitude, longitude) {
        const darkskyAPI = "9a03992fa5a35806ef16e39dbe970fdc";
        const DarkSkyUrl = "https://api.darksky.net/forecast/" +
            darkskyAPI + "/" +
            latitude + "," +
            longitude;

        const error = https.get(DarkSkyUrl, (response) => {
            let data = "";
            response.on('data', (d) => {
                data += d.toString();
            });

            response.on("end", (d) => {
                const weatherInfo = JSON.parse(data);
                const currentInfo = weatherInfo.currently

                const reqData = {
                    location: location,
                    temperature: currentInfo.temperature,
                    humidity: currentInfo.humidity,
                    pressure: currentInfo.pressure,
                    windSpeed: currentInfo.windSpeed
                }
                //CHANGE: Here I am emitting the 'weather' event and pass along the reqData
                this.emit('weather', reqData);
                //return reqData;
            });
        });
    }
}

module.exports = ApiMannager;

Thank you very much for your effort. I am fully satisfied and got what I wanted. Thank You!!!