sisko
sisko

Reputation: 9910

Nodejs global variable scope issue

I'm quite new to Nodejs. In the following code I am getting json data from an API.

let data_json = ''; // global variable

app.get('/', (req, res) => {
    request('http://my-api.com/data-export.json', (error, response, body) => {
        data_json = JSON.parse(body);
        console.log( data_json ); // data prints successfully
    });

    console.log(data_json, 'Data Test - outside request code'); // no data is printed
})

data_json is my global variable and I assign the data returned by the request function. Within that function the json data prints just fine. But I try printing the same data outside the request function and nothing prints out.

What mistake am I making?

Upvotes: 0

Views: 423

Answers (2)

stefan_thedev
stefan_thedev

Reputation: 187

Instead of waiting for request to resolve (get data from your API), Node.js will execute the code outside, and it will print nothing because there is still nothing at the moment of execution, and only after node gets data from your api (which will take a few milliseconds) will it execute the code inside the request. This is because nodejs is asynchronous and non-blocking language, meaning it will not block or halt the code until your api returns data, it will just keep going and finish later when it gets the response.

It's a good practice to do all of the data manipulation you want inside the callback function, unfortunately you can't rely on on the structure you have.

Here's an example of your code, just commented out the order of operations:

let data_json = ''; // global variable

app.get('/', (req, res) => {
    //NodeJS STARTS executing this code
    request('http://my-api.com/data-export.json', (error, response, body) => {
        //NodeJS executes this code last, after the data is loaded from the server
        data_json = JSON.parse(body);
        console.log( data_json );
        //You should do all of your data_json manipluation here
        //Eg saving stuff to the database, processing data, just usual logic ya know
    });

    //NodeJS executes this code 2nd, before your server responds with data
    //Because it doesn't want to block the entire code until it gets a response
    console.log(data_json, 'Data Test - outside request code'); 
})

So let's say you want to make another request with the data from the first request - you will have to do something like this:

request('https://your-api.com/export-data.json', (err, res, body) => {
    request('https://your-api.com/2nd-endpoint.json', (err, res, body) => {
         //Process data and repeat
     })
    })

As you can see, that pattern can become very messy very quickly - this is called a callback hell, so to avoid having a lot of nested requests, there is a syntactic sugar to make this code look far more fancy and maintainable, it's called Async/Await pattern. Here's how it works:

let data_json = ''

app.get('/', async (req,res) => {
    try{
        let response = await request('https://your-api.com/endpoint')
        data_json = response.body
    } catch(error) {
        //Handle error how you see fit
    }
    console.log(data_json) //It will work
})

This code does the same thing as the one you have, but the difference is that you can make as many await request(...) as you want one after another, and no nesting. The only difference is that you have to declare that your function is asynchronous async (req, res) => {...} and that all of the let var = await request(...) need to be nested inside try-catch block. This is so you can catch your errors. You can have all of your requests inside catch block if you think that's necessary.

Hopefully this helped a bit :)

Upvotes: 1

Cuong Nguyen
Cuong Nguyen

Reputation: 344

The console.log occurs before your request, check out ways to get asynchronous data: callback, promises or async-await. Nodejs APIs are async(most of them) so outer console.log will be executed before request API call completes.

let data_json = ''; // global variable
app.get('/', (req, res) => {
    let pr = new Promise(function(resolve, reject) {

        request('http://my-api.com/data-export.json', (error, response, body) => {
            if (error) {
                reject(error)
            } else {
                data_json = JSON.parse(body);
                console.log(data_json); // data prints successfully
                resolve(data_json)
            }
        });

    })
    pr.then(function(data) {
      // data also will have data_json
      // handle response here

     console.log(data_json); // data prints successfully

    }).catch(function(err) {
        // handle error here
    })

})

If you don't want to create a promise wrapper, you can use request-promise-native (uses native Promises) created by the Request module team.

Learn callbacks, promises and of course async-await.

Upvotes: 1

Related Questions