1

$(document).ready(function() {
  loadStreamInfo();
  displayAll();
});
var allStreamInfo = [{ "user": "ogaminglol"}, { "user": "faceittv" }, { "user": "twitch"}, { "user": "hearthstonesea"}, {  "user": "stephensonlance"}, {"user": "aegabriel" }];

function loadStreamInfo() {
  for (var i = 0; i < 6; i++) {

    $.ajax({
      url: ("https://wind-bow.gomix.me/twitch-api/streams/" + allStreamInfo[i].user),
      jsonp: "callback",
      dataType: "jsonp",
      success: function(data) {
        if (data.stream == null) {
          allStreamInfo[i]["status"] = "offline";
        } else {
          allStreamInfo[i]["status"] = "online";
        }
      }
    });

  }

  for (var i = 0; i < 6; i++) {
    $.ajax({
      url: ("https://wind-bow.gomix.me/twitch-api/channels/" + allStreamInfo[i].user),
      jsonp: "callback",
      dataType: "jsonp",
      success: function(data) {
        allStreamInfo[i]["logo"] = data.logo;
        allStreamInfo[i]["gameName"] = data.game;
        allStreamInfo[i]["views"] = data.views;
        allStreamInfo[i]["followers"] = data.followers;
        allStreamInfo[i]["url"] = data.url;
      }
    });
  }

}

function displayAll() {
  for (var i = 0; i < 6; i++) {
    var outString = "";
    outString += "<div class='item'>";
    outString += "<img src='" + allStreamInfo[i].logo + "' alt='logo'>";
    outString += "<a href='" + allStreamInfo[i].url + "'><span id='gameName'>" + allStreamInfo[i].gameName + "</span></a>";
    outString += "<span id='state'>" + allStreamInfo[i].status + "</span>";
    outString += "<span id='views-block'>Views:<span id='view'>" + allStreamInfo[i].views + "</span></span>";
    outString += "<span id='follow-block'>Followers:<span id='followed'>" + allStreamInfo[i].followers + "</span></span>";
    outString += "</div>";
    $("#result").append(outString);
  }
}
body {
  padding: 40px;
  ;
}
.toggle-button {
  width: 400px;
  color: white;
  height: 100px;
  text-align: center;
  margin: 0 auto;
}
.all {
  background-color: #6699CC;
  width: 30%;
  height: 70px;
  line-height: 70px;
  border-right: 2px solid grey;
  display: inline;
  float: left;
  cursor: pointer;
}
.online {
  cursor: pointer;
  line-height: 70px;
  background-color: cadetblue;
  border-right: 2px solid grey;
  width: 30%;
  height: 70px;
  display: inline;
  float: left;
}
.offline {
  cursor: pointer;
  background-color: darkorange;
  line-height: 70px;
  width: 30%;
  height: 70px;
  display: inline;
  float: left;
}
#result {
  margin-top: 30px;
}
.item {
  width: 500px;
  height: 70px;
  margin: 5px auto;
  background-color: #666699;
  border-left: 4px solid red;
  color: whitesmoke;
  /*border: 2px solid red;*/
}
a {
  text-decoration: none;
}
img {
  width: 50px;
  height: 50px;
  margin-top: 10px;
  margin-left: 20px;
  margin-right: 21px;
}
span#gameName,
span#views-block,
span#state,
span#follow-block {
  position: relative;
  bottom: 18px;
}
span#gameName,
span#state,
span#views-block {
  margin-right: 21px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<div class="toggle-button">
  <div class="all" onclick="displayAll()">All</div>
  <div class="online" onclick="displayOnline()">Online</div>
  <div class="offline" onclick="displayOffline()">Offline</div>
</div>
<div id="result">
</div>

I want dynamically add property in JSON object.I have read post1 and post2. But why I got undefined property? allStreamInfo[i]["prop"] = "value" isn't the way to add property to a object? In the debug window, there is Uncaught TypeError: Cannot set property 'logo' of undefined(…). I have check the API call,it goes well.It seems I don't define the prop,but isn't it the way to dynamically add a prop?

Community
  • 1
  • 1
Lewis
  • 45
  • 7

3 Answers3

1

As you are using $.ajax() which is an ansynchornous opertion in loop. When the get the result of ajax operation i will not have value with which it was initiated, So allStreamInfo[i] is undefined.

You can use Closures, to maintain the value of i till the ajax operation is complete

Closures are functions that refer to independent (free) variables (variables that are used locally, but defined in an enclosing scope). In other words, these functions 'remember' the environment in which they were created.

for (var i = 0; i < 6; i++) {
    (function(j) {
        $.ajax({
            url: "https://wind-bow.gomix.me/twitch-api/streams/" + allStreamInfo[j].user,
            jsonp: "callback",
            dataType: "jsonp",
            success: function(data) {
                allStreamInfo[j]["status"] = data.stream == null ? "offline" : "online";
            }
        });
    })(i);
}

Do read JavaScript closure inside loops – simple practical example

Community
  • 1
  • 1
Satpal
  • 132,252
  • 13
  • 159
  • 168
  • 1
    When the ajax operation is completed, shouldn't `i` be `6` ? I'm a new fresher,please be detailed. – Lewis Dec 12 '16 at 06:46
  • 1
    `i` will never be `6` in `(var i = 0; i < 6; i++)`. – SLePort Dec 12 '16 at 07:08
  • @SLePort But I see it in the debug window? – Gaby Dec 12 '16 at 12:57
  • @SLePort and Gaby its due to [hoisting](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/var#var_hoisting) and [example](https://jsfiddle.net/eodLk9oe/) – Satpal Dec 12 '16 at 13:02
  • I get it. Thanks @Satpal. – SLePort Dec 12 '16 at 13:14
  • Would you please explain the basic principle of `(function(j) {...}(i);` , How could it maintain the the `i` value? – Gaby Dec 12 '16 at 13:19
  • @Gaby, Go through the link I have provided. its complex topic – Satpal Dec 12 '16 at 13:23
  • I have read it. I mean the sytax. `(function(j) {...})(i);` means everytime the `i` value will pass into j,and the `ajax` will remember that `j`? – Gaby Dec 12 '16 at 13:26
  • @gaby thats called _immediately invoked function expression_ abbr IIFE see http://stackoverflow.com/questions/8228281/what-is-the-function-construct-in-javascript – Satpal Dec 12 '16 at 13:28
  • Got it ! Last question: By Using IIFE,will the second-time Ajax call has to wait ,before finishing the first-time Ajax call's `success callback` function ? – Gaby Dec 12 '16 at 13:50
  • @Gaby, Answer is No, they are asynchronous call – Satpal Dec 12 '16 at 13:50
  • Thanks, You are a great help ! – Gaby Dec 12 '16 at 13:52
  • There is no error message about that problem,so I close the computer yesterday. But this morning still get a `undefined:1 GET http://localhost:63342/Twitchtv/undefined 404 (Not Found)` , and the page shows like before. why? – Lewis Dec 13 '16 at 04:02
  • @Lewis Above problem has nothing to do with solution and you must have bug in the system – Satpal Dec 14 '16 at 08:06
1

Well, above all, your code doesn't seem right to me. $.ajax returns a promise, which is a future object.

i becomes 6 when these promises are resolved (the loop terminates when i == 6). So in the success callback the expression becomes allStreamInfo[6]["status"].

Since allStreamInfo[6] is undefined in your code, the error is thrown.

31piy
  • 23,323
  • 6
  • 47
  • 67
  • What is a future object? – Gaby Dec 12 '16 at 11:11
  • A future object is something, which cannot immediately return the required value, because the value isn't available right now. The HTTP call is the best example for it, because you know, you cannot be sure when the server will return the requested data. [More details here](https://en.wikipedia.org/wiki/Futures_and_promises). – 31piy Dec 12 '16 at 11:14
  • Because promises are resolved asynchronously, which takes a significant delay (which is enough for the loop to complete). – 31piy Dec 12 '16 at 11:20
  • oh! now I understand. Would you please explain [This Post](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example/) for me? The example code in this question has no `ajax`, but why the `i` still be the upper limit ? – Gaby Dec 12 '16 at 11:26
  • You may want to read more about Variable Hoisting, and Closures in JavaScript. It will give you clear understanding of what's going on there. :) – 31piy Dec 12 '16 at 11:29
  • I still get the a [error message](http://stackoverflow.com/questions/41113831/undefined1-get-http-localhost63342-twitchtv-undefined-404-not-found) after fix this bug. – Lewis Dec 13 '16 at 05:21
0

you dealing with promises try this code

$(document).ready(function() {
  loadStreamInfo();
});
var allStreamInfo = [{
  "user": "ogaminglol"
}, {
  "user": "faceittv"
}, {
  "user": "twitch"
}, {
  "user": "hearthstonesea"
}, {
  "user": "stephensonlance"
}, {
  "user": "aegabriel"
}];


var i = 0;

function loadStreamInfo() {


  $.ajax({
    url: ("https://wind-bow.gomix.me/twitch-api/channels/" + allStreamInfo[i].user),
    jsonp: "callback",
    dataType: "jsonp",
    success: function(data) {

      if (data.stream == null) {
        allStreamInfo[i].status = "offline";
      } else {
        allStreamInfo[i].status = "online";
      }

      allStreamInfo[i].logo = data.logo;
      allStreamInfo[i].gameName = data.game;
      allStreamInfo[i].views = data.views;
      allStreamInfo[i].followers = data.followers;
      allStreamInfo[i].url = data.url;

      i++;
      if (i < allStreamInfo.length)
        loadStreamInfo();
      else
        displayAll();
    }
  });

}

function displayAll() {
  for (var i = 0; i < 6; i++) {
    var outString = "";
    outString += "<div class='item'>";
    outString += "<img src='" + allStreamInfo[i].logo + "' alt='logo'>";
    outString += "<a href='" + allStreamInfo[i].url + "'><span id='gameName'>" + allStreamInfo[i].gameName + "</span></a>";
    outString += "<span id='state'>" + allStreamInfo[i].status + "</span>";
    outString += "<span id='views-block'>Views:<span id='view'>" + allStreamInfo[i].views + "</span></span>";
    outString += "<span id='follow-block'>Followers:<span id='followed'>" + allStreamInfo[i].followers + "</span></span>";
    outString += "</div>";
    $("#result").append(outString);
  }
}
body {
  padding: 40px;
  ;
}
.toggle-button {
  width: 400px;
  color: white;
  height: 100px;
  text-align: center;
  margin: 0 auto;
}
.all {
  background-color: #6699CC;
  width: 30%;
  height: 70px;
  line-height: 70px;
  border-right: 2px solid grey;
  display: inline;
  float: left;
  cursor: pointer;
}
.online {
  cursor: pointer;
  line-height: 70px;
  background-color: cadetblue;
  border-right: 2px solid grey;
  width: 30%;
  height: 70px;
  display: inline;
  float: left;
}
.offline {
  cursor: pointer;
  background-color: darkorange;
  line-height: 70px;
  width: 30%;
  height: 70px;
  display: inline;
  float: left;
}
#result {
  margin-top: 30px;
}
.item {
  width: 500px;
  height: 70px;
  margin: 5px auto;
  background-color: #666699;
  border-left: 4px solid red;
  color: whitesmoke;
  /*border: 2px solid red;*/
}
a {
  text-decoration: none;
}
img {
  width: 50px;
  height: 50px;
  margin-top: 10px;
  margin-left: 20px;
  margin-right: 21px;
}
span#gameName,
span#views-block,
span#state,
span#follow-block {
  position: relative;
  bottom: 18px;
}
span#gameName,
span#state,
span#views-block {
  margin-right: 21px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<div class="toggle-button">
  <div class="all" onclick="displayAll()">All</div>
  <div class="online" onclick="displayOnline()">Online</div>
  <div class="offline" onclick="displayOffline()">Offline</div>
</div>

<div id="result">

</div>
Azad
  • 5,144
  • 4
  • 28
  • 56
  • `allStreamsInfo[i]["status"]` can be used to define property as well. There is nothing wrong with it. – 31piy Dec 12 '16 at 06:38
  • you are dealing with promises. – Azad Dec 12 '16 at 06:42
  • @azad By running your snippet,do you notice every stream is offline. That is because `https://wind-bow.gomix.me/twitch-api/channels/` has no property `stream`. So it must null. That's the reason I call the api twice to get the `online status` and `logo...`. I do as @Satpal said, but still has [a problem](http://stackoverflow.com/questions/41113831/undefined1-get-http-localhost63342-twitchtv-undefined-404-not-found). Can you have a look? – Lewis Dec 13 '16 at 05:26