等待所有 jQuery Ajax 请求完成?

How do I make a function wait until all jQuery Ajax requests are done inside another function?

In short, I need to wait for all Ajax requests to be done before I execute the next. But how?

转载于:https://stackoverflow.com/questions/3709597/wait-until-all-jquery-ajax-requests-are-done

csdnceshi78
程序go What do you mean by "done" ? I understand it as "all requests have finished either successfully or not" (resolved or rejected). But you may mean "all requests have finished successfully" (resolved). see all the variations in api.jquery.com/category/deferred-object
接近 6 年之前 回复
csdnceshi80
胖鸭 How are you calling your original ajax requests?
接近 10 年之前 回复

19个回答

jQuery now defines a when function for this purpose.

It accepts any number of Deferred objects as arguments, and executes a function when all of them resolve.

That means, if you want to initiate (for example) four ajax requests, then perform an action when they are done, you could do something like this:

$.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,            
        ...
    });
}

In my opinion, it makes for a clean and clear syntax, and avoids involving any global variables such as ajaxStart and ajaxStop, which could have unwanted side effects as your page develops.

If you don't know in advance how many ajax arguments you need to wait for (i.e. you want to use a variable number of arguments), it can still be done but is just a little bit trickier. See Pass in an array of Deferreds to $.when() (and maybe jQuery .when troubleshooting with variable number of arguments).

If you need deeper control over the failure modes of the ajax scripts etc., you can save the object returned by .when() - it's a jQuery Promise object encompassing all of the original ajax queries. You can call .then() or .fail() on it to add detailed success/failure handlers.

csdnceshi56
lrony* how to get data from this? what a hell, how this work) just a riddle
接近 2 年之前 回复
csdnceshi56
lrony* hey, buddy! can you add example with Promise? I can't understand how to resolve when success or failed =(
接近 2 年之前 回复
csdnceshi63
elliott.david Great stuff, I have been stuck on this for a while but I had missed the information where you have to return the ajax within the function, thank you for a great explanation
2 年多之前 回复
csdnceshi66
必承其重 | 欲带皇冠 Does not work when requests fail. Downvote. Will vote up, when answer is complete. This means, I want to continue only when all requests, including failed are completed.
2 年多之前 回复
csdnceshi74
7*4 Wonderful, thank you! Saved me a lot of hassle - simple, clear and works perfectly.
接近 3 年之前 回复
weixin_41568196
撒拉嘿哟木头 Just curious... how was this done before promises and the new .when() ?
接近 3 年之前 回复
csdnceshi59
ℙℕℤℝ Yes, you need to use 'apply' - see the last couple of paragraphs in the answer text.
接近 5 年之前 回复
csdnceshi53
Lotus@ This is great, but how can it be done dynamically (ie. in a loop). Can the function receive an array of functions?
接近 5 年之前 回复
weixin_41568208
北城已荒凉 i tryed it and in my console log it do two ajax call. i mean, it double all ajax call
5 年多之前 回复
csdnceshi78
程序go (I think it bears mentioning that while I understand the OP's question is about dealing with ajax calls from within a single function... I believe interpreting this as meaning "code that calls not library functions" would be too generous.)
5 年多之前 回复
csdnceshi78
程序go It is great to give people exposure to the when method, and to promises in general, but I think this isn't the best answer. If any of those ajax functions anywhere down the line create another ajax request, and then don't integrate that new promise into the chain correctly... those requests will escape this technique. For example, I can't use this technique without modifying the Shopify library I'm using for ajax add-to-cart behaviour, because it wasn't written in a 'promisy' way, and never returns the xhr objects it creates. Does this make sense? Still a great answer, though!
5 年多之前 回复
csdnceshi70
笑故挽风 when where have you been all my life
5 年多之前 回复
csdnceshi61
derek5. this is an GREAT solution.
接近 6 年之前 回复
csdnceshi52
妄徒之命 thanks for highlighting the fact that an onFailure function could be attached. As I pointed out in a comment to the OP's question: he might want to indicate more precisely what he meant by "done". "Ryan Mohr" did also have a very good point regarding the fact that fail behaves differently as done, some further reading to be done about Promises I guess html5rocks.com/en/tutorials/es6/promises
接近 6 年之前 回复
weixin_41568110
七度&光 This is excellent, thank you
大约 6 年之前 回复
csdnceshi72
谁还没个明天 Does Mootool provides a similar approach?
接近 7 年之前 回复
csdnceshi75
衫裤跑路 Be careful with the fail case. Unlike done, fail fires immediately on the first fail and disregards the remaining deferreds.
大约 7 年之前 回复
csdnceshi59
ℙℕℤℝ See linked question... it's a little complicated but can be done
7 年多之前 回复
weixin_41568131
10.24 Is it possible to bunch the requests ajax1..4 into an array and pass that?
7 年多之前 回复
weixin_41568127
?yb? This is the best solution ...simply beautifull, clean, decoupled solution :)
大约 8 年之前 回复
csdnceshi59
ℙℕℤℝ Thanks Skalee, good point - I've added that information now.
大约 8 年之前 回复
weixin_41568134
MAO-EYE This should be marked as a correct answer because it's simple, efficient and works great. Also, it should be noted that $.when returns a Promise object which has more useful methods, not only .done. For example, with .then(onSuccess, onFailure) method you could react when both requests succeed or at least one of them fails.
大约 8 年之前 回复
csdnceshi57
perhaps? This is newish and awesome :)
大约 8 年之前 回复
csdnceshi59
ℙℕℤℝ Thanks for the feedback - I've added a bit more detail.
8 年多之前 回复
csdnceshi76
斗士狗 This is indeed the best solution, but it requires a bit of explanation to the uninitiated.
8 年多之前 回复

If you want to wait until all ajax requests are finished in your document, no matter how many of them exist, just use $.ajaxStop event this way:

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

In this case, there is no need to guess how many requests can be in an application that might finish in the future. In some cases ajax requests can be part of a function's inner logic, which can be quite complicated (e.g. calling other functions), and in that case, you might not wait until said function is done with its entire logic rather than only waiting for the ajax part to complete.

$.ajaxStop here can also be bound to any HTML node that you think might be modified by ajax.

Again the purpose of this handler is to know when there is no active ajax not to clear or reset something.

P.S. If you don't mind using ES6 syntax, then you can use Promise.all for known ajax methods. Example:

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

An interesting point here is that it works both with Promises and $.ajax requests. Here is jsFiddle demonstrating the last one.

weixin_41568127
?yb? didn't know $.ajaxStop() you made my day!
2 年多之前 回复
csdnceshi73
喵-见缝插针 Actually you are not supposed to call AJAX requests inside ajaxStop, because it's meaning is to let you know when AJAX's are done. Try do $('#somebutton').click(function() { $('#somebutton).click();}). What you think, will above create forever loop ?) If yes then think the same way for ajaxStop event.
3 年多之前 回复
csdnceshi58
Didn"t forge If you call another loop of AJAX requests using this you get an endless loop. Oops. :)
3 年多之前 回复
csdnceshi64
游.程 Superb! Looking for this for ages. Thank you, sir.
大约 4 年之前 回复
csdnceshi61
derek5. exactly! this should be accepted answer.
4 年多之前 回复
weixin_41568110
七度&光 thanks, It worked with me
4 年多之前 回复
csdnceshi70
笑故挽风 You're completely right, thanks for correcting me
接近 6 年之前 回复
csdnceshi73
喵-见缝插针 You're not correct @AdrienBe, ajaxStop handles all ajax requests no matter do they succeed or not, just as proof of my words look at this jsfiddle.net/36votxba/2
接近 6 年之前 回复
csdnceshi70
笑故挽风 Assuming the original poster meant (either requests failed or successed by "done"): you might want to also use .ajaxError(), as otherwise I believe your code will not be excuted if your ajax calls fail. $(document).ajaxStop(function () { callYourFunction(); }); $(document).ajaxError(function () { callYourFunction(); }); see api.jquery.com/category/ajax/global-ajax-event-handlers
接近 6 年之前 回复
csdnceshi75
衫裤跑路 Compared to the when() solution, it has the large disadvantage not to work well together with other components, since it shares a document-wide global state. If there is some long polling going on continuously, it might even never fire.
大约 6 年之前 回复
weixin_41568131
10.24 Compared to the when() solution, it has the advantage to work even if the number of ajax calls is not known.
大约 6 年之前 回复
csdnceshi73
喵-见缝插针 Thanks for feedback, I appreciate it.
6 年多之前 回复
csdnceshi69
YaoRaoLov Valid point but it's not what the question was asking. It's not very good if you don't want to wait for all AJAX calls to return. The question is specific about waiting for the AJAX calls you've made on your own (called inside another function, as the OP wrote). Some other code may have made another AJAX call that you don't want to wait for.
接近 7 年之前 回复
weixin_41568184
叼花硬汉 +1 Much better than other answers in case you have to deal with 3rd party scripts with anonymous callbacks/closures.
接近 7 年之前 回复
csdnceshi76
斗士狗 Nice - this should have more votes
7 年多之前 回复

Also you could use async.js.

I think its better than $.when because you can merge all kinds of asynchronous call that does not support promises out of the box like timeouts, SqlLite calls etc. and not just ajax requests.

Use the ajaxStop event.

For example, let's say you have a loading ... message while fetching 100 ajax requests and you want to hide that message once loaded.

From the jQuery doc:

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

Do note that it will wait for all ajax requests being done on that page.

weixin_41568110
七度&光 Correct me if I'm wrong but won't this turn your project into an "old school web forms" site? I mean if you your entire page has to wait for a request before it can continue then what's the point of the ajax request in the first place?
5 年多之前 回复
csdnceshi52
妄徒之命 As of jQuery 1.8, the .ajaxStop() method should only be attached to document.
6 年多之前 回复
csdnceshi62
csdnceshi62 This assumes that you know there won't be any other AJAX requests on the page, not a very good assumption
接近 7 年之前 回复
csdnceshi76
斗士狗 I think, this is by far, the best solution... cheers
大约 7 年之前 回复

A little workaround is something like this:

// Define how many Ajax calls must be done
var ajaxCalls = 3;
var counter = 0;
var ajaxCallComplete = function() {
    counter++;
    if( counter >= ajaxCalls ) {
            // When all ajax calls has been done
        // Do something like hide waiting images, or any else function call
        $('*').css('cursor', 'auto');
    }
};

var loadPersons = function() {
        // Show waiting image, or something else
    $('*').css('cursor', 'wait');

    var url = global.ctx + '/loadPersons';
    $.getJSON(url, function(data) {
            // Fun things
    })
    .complete(function() { **ajaxCallComplete();** });
};

var loadCountries = function() {
    // Do things
    var url = global.ctx + '/loadCountries';
    $.getJSON(url, function(data) {
            // Travels
    })
    .complete(function() { **ajaxCallComplete();** });
};

var loadCities = function() {
    // Do things
    var url = global.ctx + '/loadCities';
    $.getJSON(url, function(data) {
            // Travels
    })
    .complete(function() { **ajaxCallComplete();** });
};

$(document).ready(function(){
    loadPersons();
    loadCountries();
    loadCities();
});

Hope can be useful...

csdnceshi76
斗士狗 Best workaround
3 年多之前 回复
csdnceshi65
larry*wei While the other answers are technically better since it's way easier to understand, I really like this one. Nice!
5 年多之前 回复

jQuery allows you to specify if you want the ajax request to be asynchronous or not. You can simply make the ajax requests synchronous and then the rest of the code won't execute until they return.

For example:

jQuery.ajax({ 
    async: false,
    //code
});
csdnceshi53
Lotus@ Definitely not a good practice but good to know
接近 2 年之前 回复
weixin_41568131
10.24 As webmaster it's my decision whether or not to give my users a potentially bad experience. The community knows NOTHING about my website, so how are they to judge how my site works? Deprecating async: false is a slap in the face of anyone who can genuinely need it.
接近 3 年之前 回复
csdnceshi61
derek5. this is the worse solution from UX perspective but solves the problem !
接近 4 年之前 回复
csdnceshi71
Memor.の Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help xhr.spec.whatwg.org
大约 4 年之前 回复
csdnceshi50
三生石@ This also doesn't work in jsonp scenarios
大约 5 年之前 回复
csdnceshi56
lrony* As stated by Alexis Wilke, async with value false is gone deprecated (by standards, not by jQuery) in most browser use cases. It may throw exceptions in newer version of browsers. See xhr.spec.whatwg.org/#sync-warning (applies to async parameter of xhr open method, which is what uses jQuery).
5 年多之前 回复
csdnceshi56
lrony* async option of jQuery is not deprecated. It is only its use combined with deferred jqXHR callbacks (done/fail/always) which is deprecated. Used with settings callbacks (success/error/complete), it still works synchronously.
5 年多之前 回复
csdnceshi73
喵-见缝插针 With HTML 5, from what I've read, some browser will drop (if not already done) the capability of sending a synchronous AJAX call. So such code will break.
5 年多之前 回复
weixin_41568183
零零乙 This is bad for gui usability
6 年多之前 回复
csdnceshi64
游.程 I wouldn't want to use your UI if you are willing to lock it up for a few seconds by making your AJAX calls synchronous. Please don't use it.
接近 7 年之前 回复
csdnceshi67
bug^君 HOW DOES THIS HAVE 21 UPVOTE... Sorry, 20 UPVOTES???
大约 7 年之前 回复
csdnceshi79
python小菜 This is a horribly bad idea. DO NOT USE THIS ANSWER.
7 年多之前 回复
csdnceshi59
ℙℕℤℝ Thanx this is what I exactly wanted.
接近 8 年之前 回复
csdnceshi70
笑故挽风 AJAX means "Asynchronous Javascript and XML", it's not supposed to be synchronous. Doing synchronous AJAX requests is not only paradoxal, but a (very) bad practice.
大约 8 年之前 回复
csdnceshi51
旧行李 Unfortunetly they removed { async : false } from 1.8 which is really bad. Sometimes you just need blocking call with optional timeout.
大约 8 年之前 回复
csdnceshi75
衫裤跑路 Particularly if the request fails or takes a long time for some unpredictable reason (which, by Murphy's Law, is bound to happen!), this is usually a bad idea for production code due to browser locking as stated above.
大约 8 年之前 回复
weixin_41568127
?yb? It's terribly bad idea! Never ever do that! Blocking = not responding to user actions at all, even to scrolling or anything! (Also, async: false is going to be deprecated in jQuery 1.8.)
大约 8 年之前 回复
csdnceshi69
YaoRaoLov This runs contrary to standard jQuery/Javascript practice. AJAX is always supposed to be asynchronous. You should use jQuery.when() instead.
8 年多之前 回复
csdnceshi66
必承其重 | 欲带皇冠 BBonifield, I am experiencing this behaviour in IE 6. Quite annoying!
8 年多之前 回复
csdnceshi77
狐狸.fox Exactly! This way you know that at the end of the function which calls all the ajax requests, that they are all finished executing.
接近 10 年之前 回复
weixin_41568174
from.. One thing to note is that using { async: false } can temporarily lock the browser. api.jquery.com/jQuery.ajax
接近 10 年之前 回复

javascript is event-based, so you should never wait, rather set hooks/callbacks

You can probably just use the success/complete methods of jquery.ajax

Or you could use .ajaxComplete :

$('.log').ajaxComplete(function(e, xhr, settings) {
  if (settings.url == 'ajax/test.html') {
    $(this).text('Triggered ajaxComplete handler.');
    //and you can do whatever other processing here, including calling another function...
  }
});

though youy should post a pseudocode of how your(s) ajax request(s) is(are) called to be more precise...

I found a good answer by gnarf my self which is exactly what I was looking for :)

jQuery ajaxQueue

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

Then you can add a ajax request to the queue like this:

$.ajaxQueue({
        url: 'page.php',
        data: {id: 1},
        type: 'POST',
        success: function(data) {
            $('#status').html(data);
        }
    });
csdnceshi79
python小菜 It looks like you've forgotten to give proper attribution to this answer, I've added it.
9 年多之前 回复

NOTE: The above answers use functionality that didn't exist at the time that this answer was written. I recommend using jQuery.when() instead of these approaches, but I'm leaving the answer for historical purposes.

-

You could probably get by with a simple counting semaphore, although how you implement it would be dependent on your code. A simple example would be something like...

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;

If you wanted this to operate like {async: false} but you didn't want to lock the browser, you could accomplish the same thing with a jQuery queue.

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();
    });
});
csdnceshi59
ℙℕℤℝ The answer is almost 6 years old. jQuery didn't have $.when() when this was written.
大约 4 年之前 回复
csdnceshi52
妄徒之命 I like your answer (although if you're using jQuery then you should use $.when()), but I'd use only the semaphore variable (making all_queued unnecesssary) by initializing semaphore with the number of AJAX requests (in your exmaple 4).
大约 4 年之前 回复
csdnceshi79
python小菜 why do we need var semaphore ? Isn't enough to have only all_queued ?
4 年多之前 回复
csdnceshi67
bug^君 Well... I'm not the one who gave you a -1... and I understand that answers do tend to age. Yet, people keep finding them and as far as I know it is not forbidden to give info to people who will potentially make use of them still today.
5 年多之前 回复
csdnceshi59
ℙℕℤℝ This is a 4.5 year old answer, and it was meant to be an example of how semaphores and queues work. You're thinking a little too hard about this, and I don't think CAPITALIZATION TO MAKE A POINT is necessary.
5 年多之前 回复
csdnceshi67
bug^君 I do not see a problem with the semaphore counter, however, I do see a problem with the idea of having FOUR functions to handle the resulting callback. You should define a function first, then reference that function in each .get(). That way at least you do not duplicate that code. Not only that but declaring a function(){} each time allocates memory each time! Rather bad practice if you could call a statically defined function.
5 年多之前 回复
csdnceshi80
胖鸭 Thanks @BBonifield for the answer - I wrote a utility function based on your answer. Refer: stackoverflow.com/questions/3709597/…
8 年多之前 回复
csdnceshi59
ℙℕℤℝ It's really not all that complicated. Counting semaphores are a common mechanism in CS. If you prefer though, the example using jQuery queues would work as well without having to implement the semaphore yourself.
接近 10 年之前 回复
csdnceshi65
larry*wei This seems like it would overly complicate a trivial problem.
接近 10 年之前 回复

I highly recommend using $.when() if you're starting from scratch.

Even though this question has over million answers, I still didn't find anything useful for my case. Let's say you have to deal with an existing codebase, already making some ajax calls and don't want to introduce the complexity of promises and/or redo the whole thing.

We can easily take advantage of jQuery .data, .on and .trigger functions which have been a part of jQuery since forever.

Codepen

The good stuff about my solution is:

  • it's obvious what the callback exactly depends on

  • the function triggerNowOrOnLoaded doesn't care if the data has been already loaded or we're still waiting for it

  • it's super easy to plug it into an existing code

$(function() {

  // wait for posts to be loaded
  triggerNowOrOnLoaded("posts", function() {
    var $body = $("body");
    var posts = $body.data("posts");

    $body.append("<div>Posts: " + posts.length + "</div>");
  });


  // some ajax requests
  $.getJSON("https://jsonplaceholder.typicode.com/posts", function(data) {
    $("body").data("posts", data).trigger("posts");
  });

  // doesn't matter if the `triggerNowOrOnLoaded` is called after or before the actual requests 
  $.getJSON("https://jsonplaceholder.typicode.com/users", function(data) {
    $("body").data("users", data).trigger("users");
  });


  // wait for both types
  triggerNowOrOnLoaded(["posts", "users"], function() {
    var $body = $("body");
    var posts = $body.data("posts");
    var users = $body.data("users");

    $body.append("<div>Posts: " + posts.length + " and Users: " + users.length + "</div>");
  });

  // works even if everything has already loaded!
  setTimeout(function() {

    // triggers immediately since users have been already loaded
    triggerNowOrOnLoaded("users", function() {
      var $body = $("body");
      var users = $body.data("users");

      $body.append("<div>Delayed Users: " + users.length + "</div>");
    });

  }, 2000); // 2 seconds

});

// helper function
function triggerNowOrOnLoaded(types, callback) {
  types = $.isArray(types) ? types : [types];

  var $body = $("body");

  var waitForTypes = [];
  $.each(types, function(i, type) {

    if (typeof $body.data(type) === 'undefined') {
      waitForTypes.push(type);
    }
  });

  var isDataReady = waitForTypes.length === 0;
  if (isDataReady) {
    callback();
    return;
  }

  // wait for the last type and run this function again for the rest of the types
  var waitFor = waitForTypes.pop();
  $body.on(waitFor, function() {
    // remove event handler - we only want the stuff triggered once
    $body.off(waitFor);

    triggerNowOrOnLoaded(waitForTypes, callback);
  });
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>Hi!</body>

</div>
共19条数据 1 尾页
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问