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

Moira Lawrie-Martyn
Moira Lawrie-Martyn
8,073 Points

NodeJS request for JSON returns undefined every time

Hi guys,

So I'm trying to get some JSON data from another url to feed into the site that I've written. I know the JSON data is there as I've pulled it via curl on the command line.

But every single time I try to run it through the website, the script crashes with an undefined error.

Here's the code that's failing:

var request = require("request");
var parse = require('xml2js');

function getFile(year){
    var data;
    if(year < 2010)
    {
        var filename = year + ".xml";
    }
    else
    {
        var filename = year + ".json";
    }

    var url = <url> + filename;

    request(url, function(error, response){
        if(error){
            data = fs.readFileSync("./data/" + filename, "utf8");
        }
        else
        {
            data = response;
            console.log("call successful");
        }
    });

    if(year < 2010)
    {
        data = parse.toJson(data); // for the few that are xml files
    }

    return data;
}

I've done everything I can think of to try and get it working. I really don't know what to do next. Can anyone help?

1 Answer

Michael Hulet
Michael Hulet
47,912 Points

Is your script crashing, or is it just returning undefined every time? If it's crashing, that's a different issue, and an error log would be helpful, but it looks to me like it's probably just returning undefined. This is because request is an asynchronous function, which means that when you call it, your script won't wait for it to finish, and it'll keep on running all the code starting with if(year < 2010) immediately, before the data you request is downloaded. It'll get to return data before the request finishes, and at that point, data will still be undefined, so your function will always return undefined

Ultimately, since request is asynchronous and your function won't wait for it, there's no good way to make this function return meaningful data. What you'll likely need to do is make your function accept a callback function as a second argument, and instead of returning data from getFiles, pass the data you generate as an argument to the callback function that you pass it

Moira Lawrie-Martyn
Moira Lawrie-Martyn
8,073 Points

Hi Michael, it looks like this is definitely the issue. I'm not used to dealing with async so it's throwing the rest of my code out. I've just got to work out how to build a callback in. Back to the drawing board >.<

Michael Hulet
Michael Hulet
47,912 Points

Don't go back to the drawing board! Your code actually looks super solid rn. Asynchronous code is super hard for everyone, especially when you're first starting out, but I even everyone where I work has to stop and think about it for a second when they're dealing with it. Making a callback looks something like this:

function myAsyncFunction(someData, callback){
    requestDataFromNetwork("https://example.com", function (error, response){
         // Do whatever you want with the response before "returning" it
         callback(response);
    }
}

Later on, calling that function looks something like this:

myAsyncFunction(null, function(networkData) {
    console.log(networkData); // Log all the downloaded data
}

You can think of that callback function sort of like an asynchronous return mechanism, but one that just works when it's ready. You're already using that pattern when you call request, you just have to make your function do something similar

Newer versions of Node, however, support a syntax called async/await, which is built on top of another abstraction called Promises. There's a different version of the request library that uses them instead of callbacks. Basically, it allows you to write code that runs asynchronously, but looks like it's running like a normal synchronous function, like this:

const request = require("request-promise-native"); // Notice the different library up here

async function myAsynchronousFunction(someData){ // Notice the async keyword here
    try{
        return await request("https://example.com"); // Notice the await keyword here
    }
    catch(error){
        // Handle any potential errors here
    }
}

The async keyword tells JavaScript that it can pause your function until some of the calls it makes finishes, and the await keyword tells JavaScript to wait for a particular function call to finish before moving on with the rest of your function. Basically, it's all the benefits of asynchronous code without all of the mental difficulty of thinking about it. Later on in another async function, you can call your function like this:

const networkData = await myAsynchronousFunction(null);
// Do whatever you want with the downloaded data here