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

Danny L.
Danny L.
10,837 Points

Astronaut code. I barely understood anything, I'm really lost

The code works fine and I get the correct results but I just don't understand why.

I'm going through the Full Stack Javascript track and I also did the separate Callback function course and I was following along fine when we needed to call the getJSON function with the astrosUrl variable.

I understood adding the event listener.

Then things started to go south for me when we added the callback here:

function getJSON(url, callback) {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.onload = () => {
    if(xhr.status === 200) {
      let data = JSON.parse(xhr.responseText);
      return callback(data);
    }

I don't understand what callback is doing. We didn't even create a function called callback right?

I also got lost with the (json) parameter here:

getJSON(astrosUrl, (json) => {
    json.people.map(person => {
      getJSON(wikiUrl + person.name, generateHTML);
    })

I have no idea what is going on here. What is the (json) parameter?

I'm feeling really lost and a little discouraged, if anyone could help me understand this I would greatly appreciate it.

7 Answers

No expert myself, but I can sure try to explain better. Hope I don't confuse you more :P I'll try my best.

In javascript you call a function (execute the code inside the function) when you have the () after the name, with or without arguments inside ().

Example:

  • hello() -> calls the function 'hello' (execute the code inside the function)
  • hello(data) -> calls the function 'hello' with argument (execute the code inside the function)
  • hello -> refers to the function 'hello' (don't execute the code inside the function)

So in your code:

// here you create/define (not calling) the function getJSON.
// You define two parameters (url and callback) 
// that only is known to your getJSON function, not outside it.
function getJSON(url, callback) { 
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = () => {
        if(xhr.status === 200) {
            let data = JSON.parse(xhr.responseText);
            // Here you call (run/invoke) the callback function 
            // since you added the (data), e.g. () as explained on top.
            return callback(data); 
        }
    }
}


// So you don't have to define 'callback' anywhere, 
// because it's just referring to whatever function you pass in as 
// an argument later (e.g. 'callbackFunction')
// You could call the 'callback' parameter 'help' or 'damn' if you'd like to, 
// it doesn't matter what you call it.


// So.. Now, when you are calling (invoke/run) your getJSON function, like this:
getJSON(astrosUrl, callbackFunction); 
// Think here that you assign url = astrosUrl, and callback = callbackFunction, in your
// getJSON function!
// Since you here pass 'astrosUrl' and 'callbackFunction' as arguments -> these must be defined!
// But you are not calling the callbackFunction with (), 
// because you do not want to run it immediately, which would 
// happen if you had passed callbackFunction() as argument, e.g.:
// getJSON(astrosUrl, callbackFunction(<some variable with data>)); 

// and here is your defined callbackFunction(json). 
// Also here (like in function getJSON) you can name 
// your parameter (json) whatever you want (but json === data in your case)
function callbackFunction(json) { //
    json.people.map(mapFunction);
}


// Now you can think of it like this when running your script:

    // Calling:
    getJSON(astrosUrl, callbackFunction) { // just referring to callbackFunction
        const xhr = new XMLHttpRequest();
        xhr.open('GET', astrosUrl);
        xhr.onload = () => {
            if(xhr.status === 200) {
                let data = JSON.parse(xhr.responseText);
                return callbackFunction(data); // here you are running the callbackFunction!
                // so you must define an argument (e.g. data).
            }
        }
    }

  // So when you are running the function, you could see that
  // atrosUrl is the url, and callbackFunction is the callback.
  // then later you can call the getJSON function again, with
  // maybe other arguments! Like:
  // getJSON(anotherUrl, anotherCallbackFunction)

    // here is the function call from above:
    callbackFunction(data) {
        data.people.map(mapFunction);
    }

To explain the last code you provided, try to look at the following image: forEach call

When reading my comment now, I start to confuse myself - haha. So do not worry if you do not understand.. :P Too much beer tonight!

I do not know how the whole code is structured, but an AJAX-request (your getJSON-function) is asynchronous, that means that a browser can make a request to a server without stopping the functionality of a webpage - e.g. click on buttons and so on - while the browser is waiting for a response from the server in the AJAX-call. The callback-function is executed when a response is getting received from the server. This can take 1 second, or maybe 10 seconds, therefore it's nice that the whole page not freezes when it's waiting for that piece of data.

I understand that the code you showing are confusing, not really readable in my opinion.

But to simplify the code, you can write the arrow-functions as regular functions.

// This is your provided function call
getJSON(astrosUrl, (json) => { 
    json.people.map(person => {
      getJSON(wikiUrl + person.name, generateHTML);
    })

/************************************************************/

// can be written as this:
getJSON(astrosUrl, callbackFunction);

function callbackFunction(json) {
    json.people.map( person => {
      getJSON(wikiUrl + person.name, generateHTML);
    });
}

/************************************************************/

// Or even simpler:
getJSON(astrosUrl, callbackFunction);

function callbackFunction(json) {
    json.people.map(mapFunction);
}

function mapFunction(person) {
    getJSON(wikiUrl + person.name, generateHTML);
}

To comment the actions taken in your code:

// Here your define your AJAX-function. It takes a URL (GET-request) 
// and a function (callback-function) as parameters.
function getJSON(url, callback) {
    const xhr = new XMLHttpRequest();
    // opens a connection to the url your want to get data from
    xhr.open('GET', url);
    // waiting for response from server
    xhr.onload = () => {
        // the request was succesful
        if(xhr.status === 200) {
            // this takes the response and transfer it to an JavaScript object
            let data = JSON.parse(xhr.responseText);
            // running the callback-function with the response data
            return callback(data);
        }
    }
}

// Here you call your AJAX-function, with "astrosUrl" as the URL you want to get some data from, 
// and "callbackFunction" as the callback function that you want 
// to run when you receive a response from the server.
getJSON(astrosUrl, callbackFunction);


// When response is reveived from server "callbackFunction" 
// is runned with the response data (json parameter) that you parsed with the 
// "let data = JSON.parse(xhr.responseText)" in your AJAX-function. 
// json is here a javascript object with an array of people, 
// and the function goes through every person object with the "map()" function. 
// So for every person object, the "mapFunction" is runned.
function callbackFunction(json) {
    json.people.map(mapFunction);
}

// When a response is reveived and "callbackFunction" is runned, 
// then "mapFunction" is runned with the person object as parameter. 
// This triggers a new AJAX-call for that person-object, 
// and then when this reponse is recieved, the "generateHTML"-function is runned, 
// that i assumes is generating an html-element for the person.
function mapFunction(person) {
    getJSON(wikiUrl + person.name, generateHTML);
}

So in total you are running two callback-functions (callbackFunction and generateHTML).

I hope I did not mislead you with this explanation, but I think that's how it works.

Danny L.
Danny L.
10,837 Points

Ok wow thank you so much Jonathan, I'm still not 100% but I have never been closer than after reading your explanation. Thank you so much!

As for what I didn't understand, I hope you don't mind me asking for some clarification.

In this code here, I understand that we have callback as a parameter but I don't see where it gets defined. If we just call something callback and put it as a parameter, don't we need to explain what the function will do?

// Here your define your AJAX-function. It takes a URL (GET-request) 
// and a function (callback-function) as parameters.
function getJSON(url, callback) {
    const xhr = new XMLHttpRequest();
    // opens a connection to the url your want to get data from
    xhr.open('GET', url);
    // waiting for response from server
    xhr.onload = () => {
        // the request was succesful
        if(xhr.status === 200) {
            // this takes the response and transfer it to an JavaScript object
            let data = JSON.parse(xhr.responseText);
            // running the callback-function with the response data
            return callback(data);
        }
    }
}

Here I understood almost everything, you explained it so clearly, again, thank you so much. However I still don't understand what the (json) parameter means here. I'm assuming that is just a random name and can be called anything but still I just don't understand where the information that we are passing into (json) is coming from and how it's getting there. Apparently the (json) parameter is the same thing as data but I don't know how that happened.

// Here you call your AJAX-function, with "astrosUrl" as the URL you want to get some data from, 
// and "callbackFunction" as the callback function that you want 
// to run when you receive a response from the server.
getJSON(astrosUrl, callbackFunction);


// When response is reveived from server "callbackFunction" 
// is runned with the response data (json parameter) that you parsed with the 
// "let data = JSON.parse(xhr.responseText)" in your AJAX-function. 
// json is here a javascript object with an array of people, 
// and the function goes through every person object with the "map()" function. 
// So for every person object, the "mapFunction" is runned.
function callbackFunction(json) {
    json.people.map(mapFunction);
}

// When a response is reveived and "callbackFunction" is runned, 
// then "mapFunction" is runned with the person object as parameter. 
// This triggers a new AJAX-call for that person-object, 
// and then when this reponse is recieved, the "generateHTML"-function is runned, 
// that i assumes is generating an html-element for the person.
function mapFunction(person) {
    getJSON(wikiUrl + person.name, generateHTML);
}

To try and better breakdown my lack of understanding in this matter I wacthed this YouTube tutorial here.

In it he gives this example code:

var fruits = ["banana", "apple", "pear"];
fruits.forEach(function(val){
   console.log(val);
});

I was with him until he put in the (val) parameter. Where did that come from? What does it mean? I'm having trouble understanding how functions deal with parameters I guess.

To show with a picture: function

As said; think that url and callback, are just variables for the getJSON function, that you assign astroUrl and callbackFunction to:

  • url = astroUrl
  • callback = callbackFunction

Now you can use url and callback in the getJSON function, since they refer to astroUrl and callbackFunction!

Danny L.
Danny L.
10,837 Points

Jonathan thank you so very much! Truly! I think I finally got it. I admit you lost me a bit with the fruits example I provided but I will go back and try to understand it. But as for the original intention for this post (the functions in the Astronaut project), I think I FINALLY understand. Thank you again!

No problem! Sorry for the forEach-explanation, that was bad. I've changed it out with an image that may describe it better. Cheers!

Hi, Daniel. To really understand where the person parameter comes from, you need to understand callback functions and arrow functions.

I can provide you with an example (but also go to the links provided above):

/* First a general example of a callback function */

function greeting(name) {
  alert('Hello ' + name); // 4
}

function processUserInput(callback) {
  let name = "John"; // 2
  callback(name); // 3
}

processUserInput(greeting); // 1

// Here you:
// 1. call 'processUserInput(greeting)' 
// 2. let name = "John";
// 3. call 'callback(name)' which is 'greeting(name)' 
//     since you passed in greeting as an argument in step 1.
// 4. alert('Hello ' + name);

/*===================================================*/

/* The above can also be written as this */

function processUserInput(callback) {
  let name = "John"; // 2
  callback(name); // 3
}

processUserInput(name => { // 1
    alert('Hello ' + name); // 4
});

// The only difference here is that you are
// creating the function directly inside 'processUserInput' argument
// with an arrow function.

// Here you:
// 1. call 'processUserInput(name => { alert('Hello ' + name); });' 
// 2. let name = "John";
// 3. call 'callback(name)' which is 'name => { alert('Hello ' + name); }' 
// 4. alert('Hello ' + name);

/*===================================================*/

/* So in your case, to rename the functions: */

function map(callback) {
    // .... // 2
    // code that you cant see inside your code, 
    // but remember that is has 
    // a callback function like this: 
    callback(person); // 3
}

json.people.map(person => { // 1
    getJSON(wikiUrl + person.name, generateHTML); // 4
});

// So here, as the above example, you:
// 1. call 'map(person => { getJSON(wikiUrl + person.name, generateHTML); });' 
// 2. some code that goes through all the person objects
//     and calls the callback-function (step 3) for every person object 
// 3. call 'callback(name)' which is 'name => { getJSON(wikiUrl + person.name, generateHTML); }' 
// 4. getJSON(wikiUrl + person.name, generateHTML);

/*===================================================*/

/* And you could also write it like this (same as frist example on top): */
function findPerson(person) {
  getJSON(wikiUrl + person.name, generateHTML);
}

function map(callback) {
    // ....
    // code that you cant see, but remember that is has
    // a callback function like this:
  callback(name);
}

json.people.map(findPerson);

As I said: try to read the links I provided on top, and maybe look at my examples - hopefully you understand it a bit more. Remember things like javascript callback-functions are a bit advanced and can take a longer time to understand. The pieces will fall into place as you learn more and more ;-)

Danny L.
Danny L.
10,837 Points

Thanks so much, Jonathan, I finally understand it now, I went through a bunch of notes, read your replies, and watched a fantastic tutorial on YouTube and I think I finally have it down. You explaining it by drawing the arrows really helped. I got this. I tried to organize it visually as well as a cheatsheet. Here's what I did:

Imgur

Thanks again for all your help, Jonathan.