【问题标题】:jQuery, $.ajax with array of urlsjQuery, $.ajax 和 url 数组
【发布时间】:2020-11-13 16:20:18
【问题描述】:

我有一个简单的 url 数组,我想用 jQuery 加载每一个。我正在使用$.get,但我似乎无法让它与$.Deferred 一起工作,所以我切换到$.ajax - 我几乎让它工作了,但我得到的结果是......奇怪。我希望有人可以帮助我改进这项工作。

var results = [], files = [
   'url1', 'url2', 'url3'
];

$.when(
   $.ajax(files[0]).done(function(data) { 
      results.push(data); console.log("step 1.0"); 
   }),
   $.ajax(files[1]).done(function(data) { 
      results.push(data); console.log("step 1.1"); 
   }),
   $.ajax(files[2]).done(function(data) {
      results.push(data); console.log("step 1.2"); 
   })
).then(function(){
   console.log("step 2");
});

这应该输出..

  • 步骤 1.0
  • 步骤 1.1
  • 步骤 1.2
  • 第二步

然后results 数组包含所有 3 个 ajax 请求的结果。这可能吗?

【问题讨论】:

  • 您遇到了什么问题?请求可能以不同的顺序完成,因此您可能以step 1.2, step 1.0, step 1.1, step 2 结束,但数组将始终在then() 执行之前填充。
  • 我绝对需要他们以正确的顺序完成。这对代码正常工作很重要。
  • 我得到了不同的结果。在任何第 1 步之前,我都会收到 step 2 触发。
  • 如果您知道步数,您可以将值设置为适当的数组索引,而不是盲目地使用push
  • I need them to complete in the correct order 在这种情况下,这种模式将不适合您。请求将按顺序触发,但取决于服务器响应的顺序。正如我上面所说,这是不确定的。如果它们必须按顺序完成,您需要链接您的请求 - 也就是说,在前一个回调的回调中触发下一个请求。不过这会很慢。

标签: jquery ajax arrays promise jquery-deferred


【解决方案1】:

首先,您必须决定是要并行处理三个 ajax 调用(同时运行,总运行时间更短),还是按顺序处理一个 ajax 调用运行、完成然后启动下一个ajax调用。这是一个关键的设计决策,会影响您执行此操作的方式。

当您使用$.when() 时,您将并行启动所有三个 ajax 调用。如果仅在所有结果都完成后检查结果,您仍然可以按特定顺序处理结果(因为只有当所有结果都可用并且它们将按请求的顺序可用时才会处理它们)。但是,当这样做时,所有的 ajax 调用最初都会被立即发送。这将为您提供更好的端到端时间,因此如果这对于请求类型是可行的,这通常是一种更好的方法。

为此,您可以将所拥有的内容重组为如下所示:

并行运行

var files = [
   'url1', 'url2', 'url3'
];

$.when($.ajax(files[0]),$.ajax(files[1]),$.ajax(files[2])).done(function(a1, a2, a3) {
   var results = [];
   results.push(a1[0]);
   results.push(a2[0]);
   results.push(a3[0]);
   console.log("got all results")
});

因为您一直在等待 $.when().done() 处理程序被调用,所以所有 ajax 结果都立即准备就绪,并且它们由 $.when() 按请求的顺序呈现(不管实际上是哪个首先完成),因此您可以尽快获得结果,并且它们会以可预测的顺序呈现。

注意,我还将 results 数组的定义移到了 $.when() done 处理程序中,因为这是您知道数据实际有效的唯一地方(出于时间原因)。


并行运行 - 迭代任意长度数组

如果您有一个更长的数组,您可能会发现使用 .map() 之类的东西遍历您的数组以循环处理它们而不是单独列出它们会更好:

var files = [
   'url1', 'url2', 'url3', 'url4', 'url5', 'url6', 'url7'
];

$.when.apply($, files.map(function(url) {
    return $.ajax(url);
})).done(function() {
    var results = [];
    // there will be one argument passed to this callback for each ajax call
    // each argument is of this form [data, statusText, jqXHR]
    for (var i = 0; i < arguments.length; i++) {
        results.push(arguments[i][0]);
    }
    // all data is now in the results array in order
});

对 Ajax 调用进行排序

另一方面,如果您确实想要对 ajax 调用进行排序,以便第二个调用在第一个调用完成之前不会开始(如果第二个 ajax 调用需要来自第一个 ajax 调用的结果,则可能需要这样做)为了知道要请求什么或做什么),那么您需要一个完全不同的设计模式,$.when() 根本不是要走的路(它只做并行请求)。在这种情况下,您可能只想用x.then().then() 链接您的结果,然后您可以按照您要求的顺序输出日志语句。

  $.ajax(files[0]).then(function(data0) {
      console.log("step 1.0");
      return $.ajax(files[1]);
  }).then(function(data1) {
      console.log("step 1.1");
      return $.ajax(files[2]);
  }).done(function(data2) {
      console.log("step 1.2");
      // all the ajax calls are done here
      console.log("step 2");
  });

控制台输出:

step 1.0
step 1.1
step 1.2
step 2

如果您的文件数组较长,也可以将这个结构放入一个循环中以自动运行它以进行 N 次连续的 ajax 调用。虽然您可以在进入 results 数组时收集结果,但通常按顺序完成的原因是先前的结果被下一个 ajax 调用消耗,因此您通常只需要最终结果。如果您想随时收集结果,您当然可以在每一步将它们推送到results 数组中。

请注意,promise 提供的优势在于您可以对操作进行排序,同时保持在同一顶级嵌套级别,而不会越来越嵌套。


对 Ajax 调用进行排序 - 迭代任意长度数组

以下是循环中的排序:

var files = [
   'url1', 'url2', 'url3', 'url4', 'url5', 'url6', 'url7'
];

var results = [];
files.reduce(function(prev, cur, index) {
    return prev.then(function(data) {
        return $.ajax(cur).then(function(data) {
            console.log("step 1." + index);
            results.push(data);
        });
    })
}, $().promise()).done(function() {
    // last ajax call done
    // all results are in the results array
    console.log("step 2.0");
});

控制台输出:

step 1.0
step 1.1
step 1.2
step 1.3
step 1.4
step 1.5
step 1.6
step 2

Array.prototype.reduce() 方法在这里很方便,因为它会在您处理每个单独的数组元素时累积一个值,这是您在为每个数组元素添加 .then() 时需要执行的操作。 .reduce() 迭代以 $().promise() 的空/已解决承诺开始(还有其他方法也可以创建这样的承诺),这只是让我们开始做一些事情 .then() 已经解决了。

【讨论】:

  • 这非常有帮助。我开始看到使用外部库的智慧,即使它可能会产生开销。现在我只需要在一个地方做这件事,但如果我需要做两次或更多次,让它发挥作用的额外工作量就会变得更具破坏性。
  • @Ciel - 我不明白你的评论。为什么需要外部库?只需几行代码,您就可以将这些选项中的任何一个变成可重用的函数。
  • 对不起。我有点分心,直到今天才有机会真正尝试一下。非常感谢您的所有帮助,仅从这个示例中我就学到了很多东西。
  • 嗨@jfriend00,第二个代码sn-p 有一些问题。如果数组中只有一个 url,则返回参数数组将变为 [Object, "success", Object] 而不是 [[Object, "success", Object]]。所以在这种情况下,我们将只取第一个而不是全部取走。顺便说一句,我是另一个夏尔。哈哈。
  • @Ciel - 是的,这是$.when() 的烦恼,因为如果只传递一个项目,它的行为会有所不同。
【解决方案2】:

您应该从 .then 而不是每个 .done 访问返回值。此外,.map 是您的朋友。

var results = [], files = [
   'url1', 'url2', 'url3'
];

$.when.apply($, $.map(files, function (file) {
    return $.ajax(file);
})).then(function (dataArr) {
    /* 
     * dataArr is an array of arrays, 
     * each array contains the arguments 
     * returned to each success callback
     */
    results = $.map(dataArr, function (data) {
        return data[0]; // the first argument to the success callback is the data
    });
    console.log(results);
});

传递给 .then 的参数与传递给 .when 的顺序相同

【讨论】:

  • 我认为您没有正确处理来自$.when() 的数据。它没有给你一个数组数组的单个参数。它为您提供一系列参数,其中每个参数是三个值的数组。在第 3 行查看倒数第二个代码示例api.jquery.com/jquery.when
猜你喜欢
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-12
  • 2015-09-11
  • 1970-01-01
相关资源
最近更新 更多