TwitchTV Challenge Question

I am running the forEach loop. I want it create the script tag and run the callback function (myCallbackFunction). However, the forEach Loop only runs on the first function (createURL), when myCallbackFunction runs, it’s no longer in the forEach loop and so I lose ‘name’. If you bring up the console, you’ll see my problem.

Is there a way to loop through both functions for each of the userNames? Or any suggestions on how to use callback without calling another function? I am trying to do this in straight javascript without jQuery. Trying to learn the basics before piling on the libraries.

var userNames = [“freecodecamp”, “OgamingSC2”, “cretetion”, “storbeck”, “habathcx”, “RobotCaleb”, “ESL_SC2”];

function myFunction () {
userNames.forEach(createUrl);
}
function createUrl (name) {
tempscript = document.createElement(“script”);
tempscript.type = “text/javascript”;
tempscript.id = “tempscript”;
tempscript.src = “https://wind-bow.gomix.me/twitch-api/streams/” + name + “?callback=myCallbackFunction”;
document.body.appendChild(tempscript);
console.log(document.getElementById(“tempscript”).src);
document.body.removeChild(tempscript);

                }
                function myCallbackFunction(data) {
                        
                    console.log("name = " + name);
                    console.log("data.stream is: " + data.stream);

                        if (data.stream == null)  {
                        var status = "Offline";  
                        var h = document.createElement("div");
                        h.innerHTML = '<div class="row"><div class="col-lg-3 col-md-3 col-sm-3 col-xs-3"><div class="thumbnail"><p>' + name + '</p>' + '  ' + status + '</div></div></div>';
                                document.getElementById("content").appendChild(h); 
                                 } else { 
                                var status= "Online"; 
                                var desc = data.stream.game;
                                var onlineUrl = data.stream.channel.url;
                                var h = document.createElement("div");
                                h.innerHTML = '<div class="row"><div class="col-lg-3 col-md-3 col-sm-3 col-xs-3"><div class="thumbnail"><p>' + name + '</p>' + '  ' + status + ' ' + desc +'</div></div></div>';
                                document.getElementById("content").appendChild(h);
                                }
                        
                    }

Do you have a CodePen or Github link so we can run your code? The JavaScript interpreter in my head is a bit buggy.

@PortableStick - Here is the CodePen: https://codepen.io/PschyNet/pen/dvPwEY?editors=1010

If you bring up the console, you’ll see my problem. I am running the forEach loop. I want it create the script tag and run the callback function (myCallbackFunction). However, the forEach Loop only runs on the first function (createURL), when myCallbackFunction runs, it’s no longer in the forEach loop and so I lose ‘name’.

Is there a way to loop through both functions for each of the userNames? Or any suggestions on how to use callback without calling another function?

I am trying to do this in straight javascript without jQuery. Trying to learn the basics before piling on the libraries.

Alright, I can see what you’re trying and what you’re expecting. There are two major problems here: 1) Trying to access variables out of scope (like name), and 2) trying to make calls to remote resources by creating script tags. Problem (1) will go away when we fix problem (2).

Let’s be clear about what you’re trying. The goal is to get data from a server somewhere in the interwebs, and then use that data to populate parts of your web page. The term for this general process is AJAX. I say that the term is for the process because AJAX isn’t a language like JavaScript or a library like jQuery. There is no single way of performing AJAX. However, there are worse and better ways. The better ways will let us write clean code that flows from one step to the next in a way that can easily be described in plain English - for each user name, we send a request to the server, then we receive the response, then we process that response, and then we add that data to the web page or we handle any errors that came up. By appending script tags to your page, you’re kind of sending the request and maybe handling the response, but you’re definitely not handling it in a way you could describe with then. When will the data come back? What happens if it doesn’t come back? What do you do if you need this data in a particular order?

There’s a really simple API that you can use to make this work well: fetch. Fetch is new, but it’s going to be the standard in all browsers before too long, so it’s best to get using it now. Since it isn’t in all browsers yet, there’s a polyfill you’ll need to use to ensure support, but know that you’re not using a library. This is core JavaScript. Paste this URL into your JavaScript dependencies in CodePen to get started: https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.2/fetch.min.js

Alright, let’s talk about what to do. You’re already using forEach on your array of names - that’s great. The next step is to use fetch, which is really simple.

var url = "https://wind-bow.gomix.me/twitch-api/streams/" + name
fetch(url)
    .then(successFunction)
    .catch(handleError)

fetch is a promise, which means that it does something that could take a long time (like getting data from a server), and runs a then method only when it’s finished, whether successfully or not. catch is there to catch any errors that happen. Let’s look at what successFunction needs to do.

successFunction(response) {
    if(!response.ok) { throw new Error(response.text) } // if the request failed, send an error message to catch()
    var responseData = response.json(); // get the data object from the response so we can use it
    console.log(responseData)// This has your user data!
}

handleError only gets used if there’s a problem with the request or you throw an error.

handleError(error) {
    console.error(error);
    $('#errorMessage').text(error.message); //display something for the user
}

There’s a lot more to promises, but not much more to fetch. I have an example project for you to check out if you want a real, working app to learn from.

2 Likes

@PortableStick - Thank you for the very detailed response. I’m going to investigate fetch right now and start testing it. I’ll let you know how it goes and if I have any follow-up questions. Thanks again for taking the time to answer my question.

@PortableStick - Hey remember me? :slight_smile:

So I finally got around yesterday to testing Fetch. I have it working and returning all of the data that I need, but the data is always returned in a different order, and the data coming from the first fetch doesn’t line up with the second fetch call. I understand that the issue is that the data takes time to return and javascript doesn’t wait for it. It just keeps running and only inserts the data into my HTML after it has received it.

Run the code a few times and you’ll quckly see what I mean. The name/online/offline status, and the name/link and picture should all be together.

I can’t figure out how to make the second fetch call wait for the first fetch to complete… or am I still going about this all wrong?

An interesting quirk of promises is that if you return a promise in a promise, it will show up next in the chain. So, you can do this

fetch(url)
    .then(function(response) {
        return response.json()
    })
    .then(function(response) {
        var url = response.url
        return fetch(url)  // notice that we don't have a then function here
    })
    .then(function(newResponse) {
        // this is the response from the second fetch
    })