延迟对象使用问题jQuery

时间:2014-10-18 01:03:58

标签: javascript jquery ajax github jquery-deferred

所以我有两个AJAX调用,一个嵌套在另一个中。我们的想法是从一个URL的Github帐户中提取回购信息(标题,链接,描述),然后为所选仓库的最后10次提交提取提交信息,该提交位于不同的URL。

一切都在技术上显示,但我需要显示回购信息,然后提交信息立即显示,交替显示:

  • 回购1信息
    • 回购1提交信息
  • 回购2信息
    • 回购2提交信息
  • 回购3信息......

依此类推。我已经做了很多挖掘,并且在延迟对象上找到了很多东西,但我不知道如何根据我的情况实现它们。在线任何示例都没有任何类似于我尝试的方式。

到目前为止我的代码(github用户名是从匿名用户名中取出的):

主要功能

function main() {
  $.ajax({
    type: 'GET',
    url: 'https://api.github.com/orgs/user/repos',

    success: function(data, status){
      alert(data);
      display_content(data);
    },
    error: function(xhr, status, error){
      var err = JSON.parse(xhr.responseText);
      alert(err.Message);
    }
  });
}

回购信息功能

function display_content(_data){
  for (var i = 0; i < _data.length; i++) {
      $("#listing").append(
        "<h2>" +
          "<a href=\"" + _data[i].html_url + "\">" + _data[i].full_name + "</a></h2>" +
        "<p>" + _data[i].description + "</p><br />"
      );
      commits(_data[i].name);
    }
  }
}

提交信息功能

function commits(repo){
  $('#listing').find('h3').text("Latest Commits to user/" + repo);
  return $.ajax({
    type: 'GET',
    url: 'https://api.github.com/repos/user/' + repo + '/commits',

    success: function(commit_data, status){
      var len;

      if(commit_data.length < 10){
        len = commit_data.length;
      }else{
        len = 10;
      }

      //names of months to make date display prettier
      var m_names = new Array("January", "February", "March", 
                    "April", "May", "June", "July", "August", "September", 
                    "October", "November", "December");

      for(var i = 0; i < len; i++){
        //converting from ISO 8601 date format to my format
        var time = new Date(commit_data[i].commit.committer.date);
        var time_write = m_names[time.getMonth()] + " " + time.getDate() + ", " + time.getFullYear();

        $('#listing').append(
          "<h3> Latest Commits to user/" + repo + "</h3><br />" + 
          "<ul>" +
            "<li>" +
              "<div>" +
                "<img src=\"" + commit_data[i].author.avatar_url + "\"><br />" +
                "<a href=\"https://github.com/" + commit_data[i].author.login + "\"><br />" +
                  "<b>" + commit_data[i].author.login + "</b><br />" +
                "</a>" +
                "<b>" + time_write + "</b><br />" +
                "<i>SHA: " + commit_data[i].sha + "</i><br />" +
              "</div>" +
              "<a href=\"https://github.com/user/" + repo + "/commit/" + commit_data[i].sha + 
                "\" target=\"_blank\">" + commit_data[i].commit.message +
              "</a>" +
            "</li>" +
          "</ul>"
        );
      }
    },
    error: function(xhr, status, error){
      var err = JSON.parse(xhr.responseText);
      alert(err.Message);
    }
  });
}

如果有人可以帮我实现延迟对象,或者甚至提供更好的解决方案,那将非常感激。谢谢!

2 个答案:

答案 0 :(得分:0)

这样做的好方法似乎是获取所有数据,然后当您拥有所有数据时,您可以一次构建所有HTML。这也允许您将功能分开,您可以在一个函数中获取数据,然后在另一个函数中格式化数据。

这是使用promises获取数据的方法。我插入了github文档中的实际工作URL,所以我可以在jsFiddle中测试它:

function getRepoInfo(user) {
    var url = 'https://api.github.com/orgs/' + user + '/repos';
    return $.get(url).then(function(data) {
        // get data for all the names and return a promise when all requests are done
        return $.when.apply($, data.map(function(repo) {
            return $.get('https://api.github.com/repos/' + user + '/' + repo.name + '/commits').then(function(commit_data) {
                return {repo: repo, commits: commit_data};
            });
        }));
    });
}

getRepoInfo('octokit').then(function() {
    // put the arguments into an actual array
    var data = Array.prototype.slice.call(arguments);
    // data is an array of objects {repo: repoData, commits: [array of commit objects]}
    display_content(data);
});

目前,我还没有尝试重写您的显示功能,因为我假设一旦您拥有所有可用的数据,您就可以了解如何创建所需的HTML结构。

实际提取数据的getRepoInfo()的工作演示:http://jsfiddle.net/jfriend00/sy33sw0p/


如果您以前从未使用过承诺,这对您来说可能看起来有点希望,所以我会尝试解释,但这是相当先进的承诺使用,所以我可能不会成功解释所有。无论如何这里是:

首先,promises中的一个关键概念是,如果你在.then()处理程序中返回一个promise,那么这些promise将被链接起来,并且在新的内部promise也被解决之前,原始的promise将不会得到解决。我在这个解决方案中大量使用链接承诺的概念。第二关,$.when()返回一个承诺,它基本上是你传递的所有承诺的概要承诺。因此,如果您有N个异步操作全部同时运行,并且您想知道所有这些操作何时完成并立即收集所有结果,那么$.when()可以非常方便。在这种情况下,代码将获得一个主URL,它为我们提供了许多其他事情来进行逻辑查询,这就是它将如何流动:

Do First Query to get list of repositories
    When that finishes, fire off a new query for each repository 
    to get info about the repo
       When each of those finishes, grab data from the response and return it
    Return a new promise that is the combination of all the individual repo queries
    That new promise will be linked into the original promise and will 
    collate all the data from the individual repo queries

当第一个查询的承诺(以及定义所有其他链接的承诺)得到解决时    然后,将所有数据传递给另一个函数,以构建所需的数据DOM表示。

以下是对某些步骤的解释:

$.get(url)

原始的ajax调用是否获得了repos列表并返回了一个promise,当这个ajax调用和任何其他依赖的promise都完成时,它将被解析。

$.get(url).then(fn);

设置一个回调函数,在原始ajax调用完成时调用,并将该回调函数传递给该ajax调用的结果。

 return $.when.apply($, data.map(function(repo) {

这个有点复杂。首先发生的操作是data.map()。这将遍历原始ajax调用的所有repos,为每个repos调用一个新函数。

这个新功能是这样的:

 return $.get('https://api.github.com/repos/octokit/' + repo.name + '/commits').then(function(commit_data)

请求每个仓库的提交。完成该请求后,它会获取数据并将其格式化为表单对象{repo: repo, commits: commit_data};并返回它。这成为在data.map()调用中创建的每个promise的已解析值,并且最终将成为最外层promise中返回数据的一部分。

回到return $.when.apply($, data.map(function(repo) {data.map()会产生一个promises数组,因为每次调用时,传递给.map()的回调都会返回一个promise。然后,我们使用$.when()创建一个新的promise,当data.map() promises数组中的所有promise都完成后,它将被解析。该承诺成为getRepoInfo()函数的返回值。

答案 1 :(得分:0)

在这种方法中,所有数据一到达就会显示出来。 commits()的异步使得这有点棘手:

  • 所有 commit_data标题已写入后,<h2>返回确保到达。
  • 各种commit_data返回将以某种未知顺序返回,不一定按照请求的相同顺序返回。

所以(如你所知)只需将数据附加到#listing即可到达。

但是,可以通过预创建,在每个<h3>下面,提交数据的容器并引用可用于提交显示功能的容器来实现所需的效果这样它就知道数据到达时的位置(并且已经格式化)。

首先,将commits()拆分为AJAX部分和显示部分。这不是绝对必要的,但可以帮助您从树上看到木材。您最终得到以下四个函数:

  • main()
  • display_contents()
  • commits()
  • display_commits()

后续功能将非常简单。这样的事情(无耻地基于jFriend的octokit示例)应该这样做:

function main() {
    return $.ajax({
        type: 'GET',
        url: 'https://api.github.com/orgs/octokit/repos'
    }).then(display_content, function(xhr, status, error) {
        return JSON.parse(xhr.responseText);
    }).fail(function(err) {
        console.error(err);
    });
}
function display_content(data) {
    var promises = data.map(function(item, i) {
        var $container = $('<ul class="commits" />');
        $("#listing")
            .append("<h2><a href=\"" + item.html_url + "\">" + item.full_name + "</a></h2><div>" + item.description + "</div>") //reinsert HTML expression here
            .append( $('<h3/>').text("Latest Commits to user/" + item.name ) )
            .append($container);//placeholder for commits content that will arrive later.
        return commits(item.name).then(display_commits.bind($container), function(xhr, status, error) {
            return JSON.parse(xhr.responseText);
        });
    });
    return $.when.apply(null, promises);
}
function commits(repo) {
    return $.ajax({
        type: 'GET',
        url: 'https://api.github.com/repos/octokit/' + repo + '/commits'
    }).then(function(commit_data) {
        return { repo:repo, commit_data:commit_data }
    });
}
function display_commits(obj) {
    var commit_data = obj.commit_data,
        repo = obj.repo,
        len = Math.min(commit_data.length, 10),
        m_names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
        c, time, time_write;
    for(var i = 0; i < len; i++) {
        c = commit_data[i];
        time = new Date(c.commit.committer.date);
        time_write = m_names[time.getMonth()] + " " + time.getDate() + ", " + time.getFullYear();
        //`this` is a pre-made placeholder
        this.append("<li><div><img class=\"repo\" src=\"" + c.author.avatar_url + "\"></div><div><a href=\"https://github.com/" + c.author.login + "\">" + c.author.login + "</a></div><div>" + time_write + "<div/><div>SHA: " + c.sha + "</div><a href=\"https://github.com/user/" + repo + "/commit/" + c.sha + "\" target=\"_blank\">" + c.commit.message + "</a></li>");
    }
    return null;
}

<强> DEMO

关键位是$containerdisplay_content()的创建(并附加到DOM),以及它们与display_commits()的绑定,从而使每个$container可用thisdisplay_commits()的绑定实例。