延迟对象使用问题jQuery

Deferred Objects Usage Issue jQuery

本文关键字:问题 jQuery 对象 延迟      更新时间:2023-09-26

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

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

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

诸如此类。我已经做了很多挖掘,发现了很多关于延迟对象的内容,但我不知道如何针对我的情况实现它们。网上没有任何例子看起来和我想做的有任何相似之处

以下是我到目前为止的代码(为了匿名起见,github用户名从url中删除):

主要功能

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);
    }
  });
}

如果有人能帮助我实现延迟对象,甚至提供更好的解决方案,我将不胜感激。非常感谢。

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

  • 保证CCD_ 2返回在所有CCD_
  • 各种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;
}

演示

关键位是在display_content()中创建(并附加到DOM)$container,以及它们与display_commits()的绑定,从而使每个$container作为this可用于display_commits()的绑定实例。

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

这里有一种方法,使用promise来获取数据。我从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/


如果你以前从未使用过promise,你可能会觉得这有点希腊化,所以我会尝试解释一下,但这是一个相当高级的promise用法,所以我可能无法成功地解释所有内容。总之:

首先,promise中的一个关键概念是,如果您在commit_data0处理程序中返回promise,那么这些promise就会链接起来,并且在新的内部promise也得到解决之前,原始promise不会得到解决。我在这个解决方案中大量使用了链接承诺的概念。其次,$.when()返回一个promise,它本质上是您传递的所有promise的摘要promise。因此,如果您同时运行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

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

以下是对一些单独步骤的解释:

$.get(url)

执行原始ajax调用,该调用获取repo列表并返回一个promise,该promise将在完成此ajax调用和任何其他依赖的promise后得到解决。

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

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

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

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

这个新功能是:

 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()生成一个promise数组,因为传递给.map()的回调每次被调用时都会返回一个promise。然后,我们使用$.when()创建一个新的promise,当promise的<h2>0数组中的所有promise都完成时,该promise就会被解析。该promise将成为getRepoInfo()函数的返回值。