jQuery 中的多个同时 Ajax 请求(带一个回调函数)

Avatar of Chris Coyier
Chris Coyier 发布

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

假设您的网站上有一个功能,该功能仅在 5% 的时间内使用。 该功能需要一些 HTML、CSS 和 JavaScript 代码才能运行。 因此,您决定不将这些 HTML、CSS 和 JavaScript 代码直接放在页面上,而是在使用该功能时通过 Ajax 加载它们。

我们需要发出三个 Ajax 请求。 由于我们不想在功能准备好之前向用户显示任何内容(此外,它们都某种程度上依赖于彼此才能正常工作),因此我们需要等待所有三个请求完成后才能继续。

最好的方法是什么?

jQuery 中的 Ajax 调用提供回调函数

$.ajax({
  statusCode: {
    url: "/feature",
    success: function() {
      // Ajax success
    }
  }
});

或者“Deferred”方式,这次使用简写 $.get() 方法

$.get("/feature/").done(function() {
  // Ajax success
});

但是我们需要执行三个 Ajax 请求,并且希望在执行任何操作之前等待所有三个请求完成,因此在回调函数领域可能会变得非常复杂

// Get the HTML
$.get("/feature/", function(html) {

  // Get the CSS
  $.get("/assets/feature.css", function(css) {
    
    // Get the JavaScript
    $.getScript("/assets/feature.js", function() {

       // All is ready now, so...

       // Add CSS to page
       $("<style />").html(css).appendTo("head");

       // Add HTML to page
       $("body").append(html);

    });

  });

});

这成功地等待所有内容都准备就绪,然后才将任何内容添加到页面中。 因此,当用户看到任何内容时,它就可以使用了。 也许这会让一些人感到反胃,但我之前也做过这样的事情。 至少它是有意义的并且有效。 **问题是什么?** 它很慢。

一个请求……等待完成……另一个请求……等待完成……另一个请求……等待完成……开始。

如果我们可以这样做会更快

所有三个请求同时进行……等待所有三个请求完成……开始。

我们可以使用一些 Deferred/Promises 操作来帮助解决这个问题。 我相信这对你们中的一些人来说是 JavaScript 101 的内容,但这种事情困扰了我很久,更复杂的 Promises 内容仍然如此。

在我们简单的用例中,我们可以使用 jQuery 的 $.when() 方法,它接受这些“Deferred”对象的列表(所有 jQuery Ajax 方法都返回 Deferred 对象),然后提供单个回调函数。

$.when(

  // Deferred object (probably Ajax request),

  // Deferred object (probably Ajax request),

  // Deferred object (probably Ajax request)

).then(function() {

  // All have been resolved (or rejected), do your thing

});

因此,我们的回调地狱可以重写为

$.when(
  // Get the HTML
  $.get("/feature/", function(html) {
    globalStore.html = html;
  }),

  // Get the CSS
  $.get("/assets/feature.css", function(css) {
    globalStore.css = css;
  }),

  // Get the JS
  $.getScript("/assets/feature.js")

).then(function() {

  // All is ready now, so...

  // Add CSS to page
  $("<style />").html(globalStore.css).appendTo("head");

  // Add HTML to page
  $("body").append(globalStore.html);

});

另一个用例:芥末切割

我上面提供的用例示例是一个 5% 的功能。 为 95% 不使用该功能的用户减轻页面负担,并使其成为使用该功能的用户相对快速的附加功能。

另一种情况可能是“达到标准”的情况,在这种情况下,您可以根据需要在某些情况下向页面添加其他功能或内容。 也许对一些媒体查询执行 matchMedia 测试,并确定设备的屏幕和功能是否能够包含一些额外的模块。 太好了,使用一些并行 Ajax 调用来实现吧!