在for循环

时间:2017-02-12 12:10:20

标签: javascript jquery html api

我正在编写一个脚本,该脚本从Twitch.tv API中为数组中指定的多个通道提取数据,然后使用通道名称和流式传输来填充表格。这里是JS代码后跟HTML:



var channels = ["ESL_SC2", "freecodecamp", "OgamingSC2", "noobs2ninjas"];

$(document).ready(function() {
	$( window ).on("load", function() {
    for (var i = 0; i < channels.length; i++) {
      $.getJSON("https://wind-bow.gomix.me/twitch-api/streams/" + channels[i] + "?callback=?", function(json) {
      var json = JSON.stringify(json);
      var obj = JSON.parse(json);
      var streamStatus = obj.stream;
      if (streamStatus === null) {
        streamStatus = "Offline";
      }
      else {
        streamStatus = "Streaming " + obj.stream.channel.status;
      }
      var para = document.createElement("div");
      var node = document.createTextNode(streamStatus);
      para.appendChild(node);
      var element = document.getElementById("status");
      element.appendChild(para); 
      });
    }
    for (var i = 0; i < channels.length; i++) {
      var para = document.createElement("div");
      var node = document.createTextNode(channels[i]);
      para.appendChild(node);
      var element = document.getElementById("title");
      element.appendChild(para);
    }
  });
});
&#13;
<head>
  <script src="http://code.jquery.com/jquery-latest.js"></script>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>

<div class="row">
  <div class="col-sm-3"></div>
  <div class="col-sm-6 center-column">
    <h3>Twitch.tv Viewer</h3>
    <div class="row">
      <div class="col-sm-2" id="title"></div>
      <a href="" id="link" target="_blank" style="text-decoration:none;"><div id="status"></div></a>
    </div>
  </div>
  <div class="col-sm-3"></div>
</div>
&#13;
&#13;
&#13;

这是我的问题:每次加载页面时,第二列中的项目顺序(&#39;状态&#39; div,让您知道&#39; 39; s流式传输)以不同的顺序结束,这意味着它们不再与第一列中的频道(&#39; title&#39; div)正确对齐。

我怀疑它与API调用有关,而for循环是asynchronic,如建议here。但我无法弄清楚如何实施所提出的解决方案。任何建议将不胜感激!

5 个答案:

答案 0 :(得分:1)

嗯,是的,你是对的 - 你面临的问题是每次你在for循环中进行API调用需要花费不同的时间来处理,所以一些回调可能会更早发生另一个打破你的 for循环顺序。

使用的最佳做法是使用Promise,如下所述:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

在您当前的代码中,您可以等待所有回调完成,然后才将数据呈现给DOM(但这不是最佳做法):

&#13;
&#13;
var channels = ["ESL_SC2", "freecodecamp", "OgamingSC2", "noobs2ninjas"];

$(document).ready(function() {
	$( window ).on("load", function() {
      var getData = function(i){
        $.getJSON("https://wind-bow.gomix.me/twitch-api/streams/" + channels[i] + "?callback=?", function(json) {
        var json = JSON.stringify(json);
        var obj = JSON.parse(json);
        var streamStatus = obj.stream;
        if (streamStatus === null) {
          streamStatus = "Offline";
        }
        else {
          streamStatus = "Streaming " + obj.stream.channel.status;
        }
        channels[i].streamStatus = streamStatus;
        var para = document.createElement("div");
        var node = document.createTextNode(streamStatus);
        para.appendChild(node);
        var element = document.getElementById("status");
        element.appendChild(para); 
        if(i == channels.length -1){
          getData(++i);
        }
      });
    }
    getData(0);
    for (var i = 0; i < channels.length; i++) {
      var para = document.createElement("div");
      var node = document.createTextNode(channels[i]);
      para.appendChild(node);
      var element = document.getElementById("title");
      element.appendChild(para);
    }
  });
});
&#13;
&#13;
&#13;

在此示例中,脚本使用循环函数,只有在前一个迭代完成时才加载下一个迭代。

请注意,这会影响性能 - 因为它实际上会破坏异步API调用的完美性;这种方式仍然可以更容易地实现和管理它。

答案 1 :(得分:0)

只需在getJSON回调结束时接收下一部分数据:

function updateNextStream(){
if (i>=channels.length)return;
$.getJSON("https://wind-bow.gomix.me/twitch-api/streams/" + channels[i] + "?   callback=?", function(json) {
  var json = JSON.stringify(json);
  var obj = JSON.parse(json);
  var streamStatus = obj.stream;
  if (streamStatus === null) {
    streamStatus = "Offline";
  }
  else {
    streamStatus = "Streaming " + obj.stream.channel.status;
  }
  var para = document.createElement("div");
  var node = document.createTextNode(streamStatus);
  para.appendChild(node);
  var element = document.getElementById("status");
  element.appendChild(para);
  i++;
  updateNextStream();
  });
  }

答案 2 :(得分:0)

您可以解决此问题的一种方法是创建递归函数。这允许你等待&#34;对于每个AJAX请求,当处理请求时,它将转到获取下一个通道。

var channels = ["ESL_SC2", "freecodecamp", "OgamingSC2", "noobs2ninjas"];

var ci = 0; // Channel index
(function getChannels(){
    if(ci >= channels.length)
        return; // Stop the recursion if we've iterated all channels
    $.getJSON("https://wind-bow.gomix.me/twitch-api/streams/" + channels[ci] + "?callback=?", function(json) {
        var json = JSON.stringify(json);
        var obj = JSON.parse(json);
        var streamStatus = obj.stream;
        if (streamStatus === null) {
            streamStatus = "Offline";
        }
        else {
            streamStatus = "Streaming " + obj.stream.channel.status;
        }
        var para = document.createElement("div");
        var node = document.createTextNode(streamStatus);
        para.appendChild(node);
        var element = document.getElementById("status");
        element.appendChild(para);
        ci++;
        getChannels();
    });
    var para = document.createElement("div");
    var node = document.createTextNode(channels[ci]);
    para.appendChild(node);
    var element = document.getElementById("title");
    element.appendChild(para);
})();

https://jsfiddle.net/mnw2ojwg/

答案 3 :(得分:0)

那是因为你正在创建&#34;标题&#34;元素独立于&#34;状态&#34;元素,在API回调中创建。 API请求的完成顺序各不相同。

只需同时创建它们就不会出现这个问题。

&#13;
&#13;
var channels = ["ESL_SC2", "freecodecamp", "OgamingSC2", "noobs2ninjas"];

$(document).ready(function() {
  for (var i = 0; i < channels.length; i++) {
    $.getJSON("https://wind-bow.gomix.me/twitch-api/streams/" + channels[i] + "?callback=?", dataCallback(channels[i]));
  }
  
  function dataCallback(channel) {
    return function(data) {
      var streamStatus = data.stream;
      
      if (streamStatus === null) {
        streamStatus = "Offline";
      } else {
        streamStatus = "Streaming " + data.stream.channel.status;
      }
      
      var para = document.createElement("div");
      var node = document.createTextNode(streamStatus);
      para.appendChild(node);
      var element = document.getElementById("status");
      element.appendChild(para);

      var para = document.createElement("div");
      var node = document.createTextNode(channel);
      para.appendChild(node);
      var element = document.getElementById("title");
      element.appendChild(para);
    }
  }
});
&#13;
<head>
  <script src="http://code.jquery.com/jquery-latest.js"></script>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>

<div class="row">
  <div class="col-sm-3"></div>
  <div class="col-sm-6 center-column">
    <h3>Twitch.tv Viewer</h3>
    <div class="row">
      <div class="col-sm-2" id="title"></div>
      <a href="" id="link" target="_blank" style="text-decoration:none;">
        <div id="status"></div>
      </a>
    </div>
  </div>
  <div class="col-sm-3"></div>
</div>
&#13;
&#13;
&#13;

答案 4 :(得分:0)

正如其他人所说,问题是结果是异步返回的,但是你的逻辑依赖于它们以与调用它们相同的顺序返回。

这是一个示例解决方案,它使用返回的JSON中的ID将返回的状态与相关元素进行匹配。

状态元素与标题元素一起创建,并显示“待定”状态,直到从API返回实际状态。

var channels = ["ESL_SC2", "freecodecamp", "OgamingSC2", "noobs2ninjas"];

$(document).ready(function() {
  $(window).on("load", function() {
    for (var i = 0; i < channels.length; i++) {
      $.getJSON("https://wind-bow.gomix.me/twitch-api/streams/" + channels[i] + "?callback=?", function(json) {
        var json = JSON.stringify(json);
        var obj = JSON.parse(json);
        var streamStatus = obj.stream;
        if (streamStatus === null) {
          streamStatus = "Offline";
        } else {
          streamStatus = "Streaming " + obj.stream.channel.status;
        }

        //Update status
        var channelId = '';
        if (obj._links && obj._links.channel) {
          const parts = obj._links.channel.split('/');
          if (parts.length >= 6) {
            channelId = parts[5];
          } 
        }

        var statusEl = document.getElementById("channel_"+channelId);
        if (statusEl) {
          statusEl.innerHTML = streamStatus;
        }
      });
    }
    
    //cache relevant elements
    var titleEl = document.getElementById("title");
    var statusEl = document.getElementById("status");
    for (var i = 0; i < channels.length; i++) {
      var para = document.createElement("div");
      var node = document.createTextNode(channels[i]);
      para.appendChild(node);
      titleEl.appendChild(para);
      
      //create status elements
      para = document.createElement("div");
      para.id = "channel_"+channels[i];
      node = document.createTextNode('pending...');
      para.appendChild(node);
      statusEl.appendChild(para);
    }
  });
});
<head>
  <script src="http://code.jquery.com/jquery-latest.js"></script>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>

<div class="row">
  <div class="col-sm-3"></div>
  <div class="col-sm-6 center-column">
    <h3>Twitch.tv Viewer</h3>
    <div class="row">
      <div class="col-sm-2" id="title"></div>
      <a href="" id="link" target="_blank" style="text-decoration:none;">
        <div id="status"></div>
      </a>
    </div>
  </div>
  <div class="col-sm-3"></div>
</div>