【问题标题】:How to run code after separate ajax calls have completed within an each loop如何在每个循环中完成单独的 ajax 调用后运行代码
【发布时间】:2017-06-06 04:29:45
【问题描述】:

我的 HTML 代码:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
    <ul id="output">
    <template id="list-template">
      <a href="{{url}}" target="_blank">
        <li>
          <p><strong>{{name}}</strong></p>
          <p><img src="{{logo}}" alt="{{name}} logo"></p>
        </li>
      </a>
    </template>
  </ul>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.js"></script>

</body>
</html>

我的 JS 代码:

 $( document ).ready(function() {

  var $ul = $('#output');
  var listTemplate = $('#list-template').html();
  var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
  var allStreams = [];
  var arr = [];


  $.each(channels, function(i, channelName){
    var xhr = $.ajax({
      url: 'https://wind-bow.gomix.me/twitch-api/streams/' + channelName,
      dataType: 'jsonp',
      success: function(data) {

        if (data.stream) { // i.e. if it's not null and currently streaming
          allStreams[i] = {
            name: data.stream.channel.display_name,
            url: data.stream.channel.url,
            logo: data.stream.channel.logo,
            status: data.stream
          };
        } else { // i.e. it's not currently streaming, do a separate request to get the channel info.
          $.ajax({
            url: 'https://wind-bow.gomix.me/twitch-api/channels/' + channelName,
            dataType: 'jsonp',
            success: function(channelData) {
              allStreams[i] = {
                name: channelData.display_name,
                url: channelData.url,
                logo: channelData.logo
              };
            } // close inner success
          }); // close inner $.ajax()
        } // close else
      } // close outer success
    }); // close outer $.ajax()
    arr.push(xhr);


  }); // close $.each()

  console.log(allStreams);

  $.when.apply($, arr).then(function(){
    $.each(allStreams, function(i, stream) {
      $ul.append(Mustache.render(listTemplate, stream));
    });
  })

/* deleted accounts
  - brunofin
  - comster404
*/



}); // close .ready()

在外部和内部 ajax 请求完成所有迭代后,我需要运行以下代码:

$.each(allStreams, function(i, stream) {
  $ul.append(Mustache.render(listTemplate, stream));
});

您可能会注意到,我已尝试在此处实施建议:Is it possible to run code after all ajax call completed under the for loop statement?

...但这似乎只在 $.each() 循环中只有一个 ajax 调用时才有效。

如何在我的 $.each() 循环中处理两个单独的 ajax 请求,每个请求都有多次迭代?目前只有外部 ajax 迭代提供的实时流显示在我的列表中。

【问题讨论】:

  • 代码不是那么大 - 把它放在问题中

标签: javascript ajax each


【解决方案1】:

利用 $.ajax 为您带来承诺的事实。

在 .then 回调中,返回一个值会解析 .then 返回的承诺 - 然而,返回一个 Promise 将意味着 .then 将等待返回的 Promise 并解析为该值

这也意味着,通过使用 Array#map,无需推送到数组或 array[i] = 任何类型的代码 - 这一切都由 .map 和 promise 的解析值处理

$(document).ready(function() {
    var $ul = $('#output');
    var listTemplate = $('#list-template').html();
    var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
    var arr = channels.map(function(channelName) {
        return $.ajax({
            url: 'https://wind-bow.gomix.me/twitch-api/streams/' + channelName,
            dataType: 'jsonp'
        }).then(function(data) {
            if (data.stream) { // i.e. if it's not null and currently streaming
                return {
                    name: data.stream.channel.display_name,
                    url: data.stream.channel.url,
                    logo: data.stream.channel.logo,
                    status: data.stream
                };
            } else { // i.e. it's not currently streaming, do a separate request to get the channel info.
                return $.ajax({
                    url: 'https://wind-bow.gomix.me/twitch-api/channels/' + channelName,
                    dataType: 'jsonp'
                }).then(function(channelData) {
                    return {
                        name: channelData.display_name,
                        url: channelData.url,
                        logo: channelData.logo
                    };
                });
            }
        });
    });

    $.when.apply($, arr).then(function() {
        var allStreams = [].slice.call(arguments);
        $.each(allStreams, function(i, stream) {
            $ul.append(Mustache.render(listTemplate, stream));
        });
    });
});

【讨论】:

  • 感谢 Jaromanda X,这似乎是一些高级别的东西。我一直在努力填补关于承诺的知识空白,以便我能正确理解您的答案。但是,当我插入您的代码时,我收到错误:jQuery.Deferred 异常:无法读取未定义的属性“流” TypeError:无法读取未定义的属性“流”...我注意到您正在尝试分配结果[0] 到 data 变量,但是 results 不是一个对象吗?
  • 我似乎已经通过删除这两行来运行您的代码: var channelData = results[0]; var channelData = 结果[0];并将结果变量分别更改为“数据”和“通道数据”。虽然它有效,但我想了解删除的代码的用途是什么?
  • 该死的......我希望我能直接在我的脑海中得到 jQuery 的承诺!也许值在不同版本或其他东西中有所不同 - 对此感到抱歉
  • 我已经编辑了答案,所以它是正确的......我确定我之前编辑过一个类似的答案,但我必须做不同的:p
【解决方案2】:

我正在考虑使用基于 promise 的解决方案,但您的场景似乎可以通过添加 2 个 if 子句来解决(如果使用 promise,可以减少到 1 个)。我已将您的渲染逻辑放在一个函数中,并在所有 ajax 请求完成时调用它。

 $( document ).ready(function() {

var $ul = $('#output');
var listTemplate = $('#list-template').html();
var channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
var allStreams = [];
var arr = [];

$.each(channels, function(i, channelName){

var xhr = $.ajax({
  url: 'https://wind-bow.gomix.me/twitch-api/streams/' + channelName,
  dataType: 'jsonp',
  success: function(data) {

    if (data.stream) { // i.e. if it's not null and currently streaming
      allStreams[i] = {
        name: data.stream.channel.display_name,
        url: data.stream.channel.url,
        logo: data.stream.channel.logo,
        status: data.stream
      };
      if(channels.length === allStreams.length){
        allStreamsFetched();
      }

    } else { // i.e. it's not currently streaming, do a separate request to get the channel info.
      $.ajax({
        url: 'https://wind-bow.gomix.me/twitch-api/channels/' + channelName,
        dataType: 'jsonp',
        success: function(channelData) {

          allStreams[i] = {
            name: channelData.display_name,
            url: channelData.url,
            logo: channelData.logo
          };

          if(channels.length === allStreams.length){
            allStreamsFetched();
          }
        } // close inner success
      }); // close inner $.ajax()
    } // close else
  } // close outer success
}); // close outer $.ajax()
arr.push(xhr);


}); // close $.each()


function allStreamsFetched(){
 console.log(allStreams);
 $.each(allStreams, function(i, stream) {
 $ul.append(Mustache.render(listTemplate, stream));
});
 }

});

【讨论】:

  • I was thinking of using promise based solution - 你的意思就像问题本身一样......$.when 是 Promise.all 的 jQuery Promise“版本”:p
  • if(channels.length === allStreams.length){ 也不能保证,如果说最后一次调用“流”得到一个流,但是说倒数第二个没有并且仍然需要等待“通道”完成... allStreams.length 将等于 channels.length,但是 allStreams 中倒数第二个“插槽”将为空 - 您需要引入一个单独的计数器,当添加 allStreams 项目时递增跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-02
  • 2018-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多