等到所有 jQuery Ajax 请求完成?

IT技术 javascript jquery ajax
2021-01-12 20:48:34

如何让函数等待所有 jQuery Ajax 请求在另一个函数内完成?

简而言之,在执行下一个请求之前,我需要等待所有 Ajax 请求完成。但是如何?

6个回答

jQuery 现在为此定义了一个when 函数

它接受任意数量的 Deferred 对象作为参数,并在它们全部解析时执行一个函数。

这意味着,如果您想发起(例如)四个 ajax 请求,然后在完成后执行一个操作,您可以执行以下操作:

$.when(ajax1(), ajax2(), ajax3(), ajax4()).done(function(a1, a2, a3, a4){
    // the code here will be executed when all four ajax requests resolve.
    // a1, a2, a3 and a4 are lists of length 3 containing the response text,
    // status, and jqXHR object for each of the four ajax calls respectively.
});

function ajax1() {
    // NOTE:  This function must return the value 
    //        from calling the $.ajax() method.
    return $.ajax({
        url: "someUrl",
        dataType: "json",
        data:  yourJsonData,            
        ...
    });
}

在我看来,它提供了干净清晰的语法,并避免涉及任何全局变量,例如 ajaxStart 和 ajaxStop,这可能会在您的页面开发过程中产生不必要的副作用。

如果您事先不知道需要等待多少个 ajax 参数(即您想使用可变数量的参数),它仍然可以完成,但只是有点棘手。请参阅将延迟数组传递给 $.when()(也可能是jQuery .when 使用可变数量的参数进行故障排除)。

如果您需要更深入地控制 ajax 脚本等的失败模式,您可以保存返回的对象.when()- 它是一个包含所有原始 ajax 查询的 jQuery Promise对象。您可以调用.then().fail()来添加详细的成功/失败处理程序。

@skalee 感谢您强调onFailure可以附加功能的事实正如我在对 OP 问题的评论中指出的那样:他可能想更准确地指出他所说的“完成”是什么意思。“Ryan Mohr”对于fail行为不同的事实也有一个很好的观点,done关于Promises我猜html5rocks.com/en/tutorials/es6/promises的一些进一步阅读
2021-03-25 20:48:34
让人们接触 when 方法和一般的 promises 很好,但我认为这不是最好的答案。如果这些 ajax 函数中的任何一个创建了另一个 ajax 请求,然后不正确地将该新Promise集成到链中......这些请求将逃避这种技术。例如,如果不修改我用于 ajax 添加到购物车行为的 Shopify 库,我就不能使用这种技术,因为它不是以“Promise”的方式编写的,并且从不返回它创建的 xhr 对象。这有意义吗?尽管如此,仍然是一个很好的答案!
2021-03-26 20:48:34
这应该被标记为正确答案,因为它简单、高效且效果​​很好。另外,应该注意的是,$.when返回一个Promise对象,它具有更多有用的方法,而不仅仅是.done. 例如,使用.then(onSuccess, onFailure)方法,您可以在两个请求成功或其中至少一个请求失败时做出反应。
2021-03-29 20:48:34
小心这个fail案子。与 不同donefail在第一次失败时立即触发并忽略剩余的延迟。
2021-04-03 20:48:34
是否可以将请求 ajax1..4 组合成一个数组并传递它?
2021-04-08 20:48:34

如果您想知道文档中的所有 ajax请求何时完成,无论存在多少个请求,只需以$.ajaxStop这种方式使用事件:

$(document).ajaxStop(function () {
  // 0 === $.active
});

在这种情况下,您既不需要猜测应用程序中发生了多少请求,这些请求可能在未来完成,也不需要深入研究函数的复杂逻辑或找出哪些函数正在执行HTTP(S)请求。

$.ajaxStophere 也可以绑定到HTML您认为可能会被请求修改的任何节点。


更新:
如果你想坚持使用ES语法,那么你可以使用Promise.all已知ajax方法:

Promise.all([ajax1(), ajax2()]).then(() => {
  // all requests finished successfully
}).catch(() => {
  // all requests finished but one or more failed
})

这里有趣的一点是它同时适用于Promises$.ajax请求。

这是jsFiddle演示。


更新 2:
使用async/await语法的更新版本

try {
  const results = await Promise.all([ajax1(), ajax2()])
  // do other actions
} catch(ex) { }
+1 比其他答案要好得多,以防您必须处理带有匿名回调/关闭的 3rd 方脚本。
2021-03-22 20:48:34
与 when() 解决方案相比,它有一个很大的缺点是不能与其他组件很好地协同工作,因为它共享文档范围的全局状态。如果有一些长时间的轮询持续进行,它甚至可能永远不会触发。
2021-03-24 20:48:34
与 when() 解决方案相比,它具有即使不知道 ajax 调用次数也能工作的优势。
2021-03-26 20:48:34
@kaiser 有效点,但这不是问题所问的。如果您不想等待所有 AJAX 调用返回,那不是很好。这个问题是关于等待您自己进行的 AJAX 调用(在另一个函数中调用,如 OP 所写)。其他一些代码可能已经进行了另一个您不想等待的 AJAX 调用。
2021-03-27 20:48:34
你不正确@AdrienBe,ajaxStop 处理所有 ajax 请求,无论它们是否成功,就像我的话的证据一样,看看这个jsfiddle.net/36votxba/2
2021-04-07 20:48:34

通过gnarf my self找到了一个很好的答案这正是我正在寻找的 :)

jQuery ajax队列

//This handles the queues    
(function($) {

  var ajaxQueue = $({});

  $.ajaxQueue = function(ajaxOpts) {

    var oldComplete = ajaxOpts.complete;

    ajaxQueue.queue(function(next) {

      ajaxOpts.complete = function() {
        if (oldComplete) oldComplete.apply(this, arguments);

        next();
      };

      $.ajax(ajaxOpts);
    });
  };

})(jQuery);

然后你可以像这样向队列添加一个ajax请求:

$.ajaxQueue({
        url: 'page.php',
        data: {id: 1},
        type: 'POST',
        success: function(data) {
            $('#status').html(data);
        }
    });
看起来您忘记对这个答案进行适当的归因,我已经添加了它。
2021-03-27 20:48:34

使用ajaxStop事件。

例如,假设您在获取 100 个 ajax 请求时有一个loading...消息,并且您想在加载后隐藏该消息。

从 jQuery文档

$("#loading").ajaxStop(function() {
  $(this).hide();
});

请注意,它将等待该页面上完成的所有 ajax 请求。

@BillRuhl 在我们的例子中,我正在循环一个 jquery 集合来构建新的东西,并且需要在完成后了解整个集合,然后再进行一些布局调整。似乎不是一个特别不寻常的案例。如果一堆其他 ajax 东西可能正在处理中,那会很糟糕,但它不会,在这里。
2021-03-12 20:48:34
从 jQuery 1.8 开始, .ajaxStop() 方法应该只附加到文档。
2021-03-13 20:48:34
这假设您知道页面上不会有任何其他 AJAX 请求,这不是一个很好的假设
2021-03-23 20:48:34
如果我错了,请纠正我,但这不会将您的项目变成“老式网络表单”站点吗?我的意思是,如果您的整个页面在继续之前必须等待请求,那么首先 ajax 请求有什么意义?
2021-03-26 20:48:34

注意:上述答案使用的功能在编写此答案时尚不存在。我建议使用jQuery.when()而不是这些方法,但我将答案留给历史目的。

——

您可能可以使用简单的计数信号量来解决问题,尽管您如何实现它取决于您的代码。一个简单的例子是这样的......

var semaphore  = 0,     // counting semaphore for ajax requests
    all_queued = false; // bool indicator to account for instances where the first request might finish before the second even starts

semaphore++;
$.get('ajax/test1.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test2.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test3.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test4.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

// now that all ajax requests are queued up, switch the bool to indicate it
all_queued = true;

如果您希望它像 {async: false} 一样操作,但又不想锁定浏览器,则可以使用 jQuery 队列完成相同的操作。

var $queue = $("<div/>");
$queue.queue(function(){
    $.get('ajax/test1.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test2.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test3.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test4.html', function(data) {
        $queue.dequeue();
    });
});
我没有看到信号量计数器的问题,但是,我确实看到使用四个函数来处理结果回调的想法存在问题。您应该先定义一个函数,然后在每个.get(). 这样至少您不会重复该代码。不仅如此,function(){}每次声明 a每次都会分配内存!如果您可以调用静态定义的函数,那么这种做法是非常糟糕的。
2021-03-18 20:48:34
嗯......我不是给你一个 -1 的人......我知道答案确实会变老。然而,人们一直在寻找它们,据我所知,不禁止向今天仍然可能使用它们的人提供信息。
2021-03-19 20:48:34
这似乎会使一个微不足道的问题变得过于复杂。
2021-03-20 20:48:34
@AlexisWilke 这是一个 4.5 年前的答案,它旨在作为信号量和队列如何工作的一个例子。您对此考虑得有点过分了,我认为没有必要使用大写字母来表示观点。
2021-03-21 20:48:34
这真的没有那么复杂。计数信号量是 CS 中的一种常见机制。如果您愿意,使用 jQuery 队列的示例也可以正常工作,而无需您自己实现信号量。
2021-03-23 20:48:34