添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

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?

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 Adriano Sep 11, 2014 at 8:50

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.

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. – skalee Jun 8, 2012 at 8:24 Be careful with the fail case. Unlike done, fail fires immediately on the first fail and disregards the remaining deferreds. – Ryan Mohr Jul 2, 2013 at 0:08 @skalee 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 – Adriano Sep 11, 2014 at 8:57 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! – Ziggy Dec 22, 2014 at 22:46

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

$(document).ajaxStop(function () {
  // 0 === $.active
  $(this).unbind('ajaxStop'); // to stop this event repeating further

In this case, neither you need to guess how many requests are happening in the application that might finish in the future, nor dig into functions' complex logic or find which functions are doing HTTP(S) requests.

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

Update:
If you want to stick with ES syntax, then you can use Promise.all for known ajax methods:

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

Interestingly, it works with Promises as well as $.ajax requests.

Here is the jsFiddle demonstration.

Update 2:
Yet more recent version using async/await syntax:

try {
  const results = await Promise.all([ajax1(), ajax2()])
  // do other actions
} catch(ex) { }
                +1 Much better than other answers in case you have to deal with 3rd party scripts with anonymous callbacks/closures.
– kaiser
                Sep 30, 2013 at 13:50
                @kaiser 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.
– Ruan Mendes
                Oct 23, 2013 at 23:24
                Compared to the when() solution, it has the advantage to work even if the number of ajax calls is not known.
– Alexis Dufrenoy
                Jun 19, 2014 at 13:06
                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.
– Bergi
                Jul 27, 2014 at 12:20
                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
– Arsen Khachaturyan
                Sep 16, 2014 at 8:40

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);
                It looks like you've forgotten to give proper attribution to this answer, I've added it.
– Tim Post
                Feb 9, 2011 at 15:15

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();
                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.
– BBonifield
                Sep 14, 2010 at 15:00
                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.
– Alexis Wilke
                Jan 12, 2015 at 4:30
                @AlexisWilke 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.
– BBonifield
                Jan 22, 2015 at 18:00
                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.
– Alexis Wilke
                Jan 23, 2015 at 9:32

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.

This assumes that you know there won't be any other AJAX requests on the page, not a very good assumption – Ruan Mendes Oct 23, 2013 at 23:27 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? – BillRuhl Jan 22, 2015 at 21:39 @BillRuhl in our case, I'm looping through a jquery collection to build new stuff and need to know about the whole collection when it's done, before making some layout adjustments. Doesn't seem like a particularly unusual case. Would be bad if a bunch of other ajax stuff might be in process, but it won't be, here. – eon Jun 13, 2019 at 18:24 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...

While the other answers are technically better since it's way easier to understand, I really like this one. Nice! – Jay Nov 28, 2014 at 19:07

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...

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
                One thing to note is that using { async: false } can temporarily lock the browser.  api.jquery.com/jQuery.ajax
– BBonifield
                Sep 14, 2010 at 14:53
                This runs contrary to standard jQuery/Javascript practice. AJAX is always supposed to be asynchronous. You should use jQuery.when() instead.
– SystemParadox
                Apr 24, 2012 at 14:50
                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.)
– skalee
                Jun 8, 2012 at 7:48
                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.
– Alex
                Jun 10, 2012 at 4:34

As other answers mentioned you can use ajaxStop() to wait until all ajax request are completed.

$(document).ajaxStop(function() {
     // This function will be triggered every time any ajax request is requested and completed

If you want do it for an specific ajax() request the best you can do is use complete() method inside the certain ajax request:

$.ajax({
    type: "POST",
    url: "someUrl",
    success: function(data) {
        // This function will be triggered when ajax returns a 200 status code (success)
    complete: function() {
        // This function will be triggered always, when ajax request is completed, even it fails/returns other status code
    error: function() {
        // This will be triggered when ajax request fail.


But, If you need to wait only for a few and certain ajax request to be done? Use the wonderful javascript promises to wait until the these ajax you want to wait are done. I made a shortly, easy and readable example to show you how does promises works with ajax.
Please take a look to the next example. I used setTimeout to clarify the example.

// Note:
// resolve() is used to mark the promise as resolved
// reject() is used to mark the promise as rejected
$(document).ready(function() {
    $("button").on("click", function() {
        var ajax1 = new Promise((resolve, reject) => {
            $.ajax({
                type: "GET",
                url: "https://miro.medium.com/max/1200/0*UEtwA2ask7vQYW06.png",
                xhrFields: { responseType: 'blob'},
                success: function(data) {
                    setTimeout(function() {
                        $('#image1').attr("src", window.URL.createObjectURL(data));
                        resolve(" Promise ajax1 resolved");
                    }, 1000);
                error: function() {
                    reject(" Promise ajax1 rejected");
        var ajax2 = new Promise((resolve, reject) => {
            $.ajax({
                type: "GET",
                url: "https://cdn1.iconfinder.com/data/icons/social-media-vol-1-1/24/_github-512.png",
                xhrFields: { responseType: 'blob' },
                success: function(data) {
                    setTimeout(function() {
                         $('#image2').attr("src", window.URL.createObjectURL(data));
                         resolve(" Promise ajax2 resolved");
                    }, 1500);
                error: function() {
                    reject(" Promise ajax2 rejected");
        var ajax3 = new Promise((resolve, reject) => {
            $.ajax({
                type: "GET",
                url: "https://miro.medium.com/max/632/1*LUfpOf7teWvPdIPTBmYciA.png",
                xhrFields: { responseType: 'blob' },
                success: function(data) {
                    setTimeout(function() {
                         $('#image3').attr("src", window.URL.createObjectURL(data));
                         resolve(" Promise ajax3 resolved");
                    }, 2000);
                error: function() {
                    reject(" Promise ajax3 rejected");
        Promise.all([ajax1, ajax2, ajax3]).then(values => {
            console.log("We waited until ajax ended: " + values);
            console.log("My few ajax ended, lets do some things!!")
        }, reason => {
            console.log("Promises failed: " + reason);
        // Or if you want wait for them individually do it like this
        // ajax1.then(values => {
        //    console.log("Promise 1 resolved: " + values)
        // }, reason => {
        //     console.log("Promise 1 failed: " + reason)
        // });
img {
  max-width: 200px;
  max-height: 100px;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>Make AJAX request</button>
<div id="newContent">
    <img id="image1" src="">
    <img id="image2" src="">
    <img id="image3" src="">

On the basis of @BBonifield answer, I wrote a utility function so that semaphore logic is not spread in all the ajax calls.

untilAjax is the utility function which invokes a callback function when all the ajaxCalls are completed.

ajaxObjs is a array of ajax setting objects [http://api.jquery.com/jQuery.ajax/].

fn is callback function

function untilAjax(ajaxObjs, fn) {
  if (!ajaxObjs || !fn) {
    return;
  var ajaxCount = ajaxObjs.length,
    succ = null;
  for (var i = 0; i < ajaxObjs.length; i++) { //append logic to invoke callback function once all the ajax calls are completed, in success handler.
    succ = ajaxObjs[i]['success'];
    ajaxObjs[i]['success'] = function(data) { //modified success handler
      if (succ) {
        succ(data);
      ajaxCount--;
      if (ajaxCount == 0) {
        fn(); //modify statement suitably if you want 'this' keyword to refer to another object
    $.ajax(ajaxObjs[i]); //make ajax call
    succ = null;

Example: doSomething function uses untilAjax.

function doSomething() {
  // variable declarations
  untilAjax([{
    url: 'url2',
    dataType: 'json',
    success: function(data) {
      //do something with success data
    url: 'url1',
    dataType: 'json',
    success: function(data) {
      //do something with success data
    url: 'url2',
    dataType: 'json',
    success: function(response) {
      //do something with success data
  }], function() {
    // logic after all the calls are completed.

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

  • // 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>
    var max= 10; for (let index = 0; index < max; index++) { var link = 'http://api.jquery.com/ajaxStop/'; var data={i:index} get_ajax(link,data,function(status, data){ console.log(index) if(size>max-2){ callback('done') size++ run_list_ajax(function(info){ console.log(info)
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>

    To expand upon Alex's answer, I have an example with variable arguments and promises. I wanted to load images via ajax and display them on the page after they all loaded.

    To do that, I used the following:

    let urlCreator = window.URL || window.webkitURL;
    // Helper function for making ajax requests
    let fetch = function(url) {
        return $.ajax({
            type: "get",
            xhrFields: {
                responseType: "blob"
            url: url,
    // Map the array of urls to an array of ajax requests
    let urls = ["https://placekitten.com/200/250", "https://placekitten.com/300/250"];
    let files = urls.map(url => fetch(url));
    // Use the spread operator to wait for all requests
    $.when(...files).then(function() {
        // If we have multiple urls, then loop through
        if(urls.length > 1) {
            // Create image urls and tags for each result
            Array.from(arguments).forEach(data => {
                let imageUrl = urlCreator.createObjectURL(data[0]);
                let img = `<img src=${imageUrl}>`;
                $("#image_container").append(img);
        else {
            // Create image source and tag for result
            let imageUrl = urlCreator.createObjectURL(arguments[0]);
            let img = `<img src=${imageUrl}>`;
            $("#image_container").append(img);
    

    Updated to work for either single or multiple urls: https://jsfiddle.net/euypj5w9/

    ........... // check array length if not "0" continue to use next array value if(ids.length) waitReq(ids.shift()); // 2 error: function(resp) .................... if(ids.length) waitReq(ids.shift()); var ids = [1, 2, 3, 4, 5]; // shift() = delete first array value (then print) waitReq(ids.shift()); // print 1 'services': { 'GetAddressBookData': function() { //This is the primary service that loads all addressbook records request = $.ajax({ type: "POST", url: "Default.aspx/GetAddressBook", contentType: "application/json;", dataType: "json" 'apps': { 'AddressBook': { 'data': "", 'Start': function() { ...services.GetAddressBookData(); request.done(function(response) { trace("ajax successful"); ..apps.AddressBook.data = response['d']; ...apps.AddressBook.Filter(); request.fail(function(xhr, textStatus, errorThrown) { trace("ajax failed - " + errorThrown);

    Worked quite nicely. I've tried a lot of different ways of doing this, but I found this to be the simplest and most reusable. Hope it helps

    1.Insert this function (and variable) into your javascript file:

    var runFunctionQueue_callback;
    function runFunctionQueue(f, index, callback) {
      var next_index = index + 1
      if (callback !== undefined) runFunctionQueue_callback = callback;
      if (f[next_index] !== undefined) {
        console.log(index + ' Next function avalaible -> ' + next_index);
        $.ajax({
          type: 'GET',
          url: f[index].file,
          data: (f[index].data),
          complete: function() {
            runFunctionQueue(f, next_index);
      } else {
        console.log(index + ' Last function');
        $.ajax({
          type: 'GET',
          url: f[index].file,
          data: (f[index].data),
          async: false,
          complete: runFunctionQueue_callback
    

    2.Buil an array with your requests, like this:

    var f = [
               {file: 'file_path', data: {action: 'action', data: 'any_data}},
               {file: 'file_path', data: {action: 'action', data: 'any_data}},
               {file: 'file_path', data: {action: 'action', data: 'any_data}},
               {file: 'file_path', data: {action: 'action', data: 'any_data}}
    

    3.Create callback function:

    function Function_callback() {
      alert('done');
    

    4.Call the runFunctionQueue function with parameters:

    runFunctionQueue(f, 0, QuestionInsert_callback);
    // first parameter: array with requests data
    // second parameter: start from first request
    // third parameter: the callback function
    

    The below solution worked for me using $when

    $.when(master.GetStateByName(stateName)).done(function(response) {
        if (response) {
    GetStateByName: function(stateName) {
        return $.ajax({
            type: 'POST',
            url: getStatesByName + '?stateName=' + stateName,
            async: false,
                    No this is not an answer, since he specifically asks for ALL requests. Your code is simple since its too simple, its only handling the response of a single request. Please read the question before answering.
    – DerDee
                    Mar 22, 2021 at 17:41
    

    Try this way. make a loop inside java script function to wait until the ajax call finished.

    function getLabelById(id)
        var label = '';
        var done = false;
        $.ajax({
           cache: false,
           url: "YourMvcActionUrl",
           type: "GET",
           dataType: "json",
           async: false,
           error: function (result) {
             label='undefined';
             done = true;
           success: function (result) {
                label = result.Message;
                done = true;
       //A loop to check done if ajax call is done.
       while (!done)
          setTimeout(function(){ },500); // take a sleep.
        return label;
                    Your setTimeout() does NOT take a sleep. In this case, you just block all tabs until done becomes true.
    – Alexis Wilke
                    Jan 12, 2015 at 4:24
                    Have you tested this code? my expectation is that done will never be true while the while loop is still running. If the while loop is running, the event loop can't continue and therefore will never run the callback to the ajax success.
    – Kevin B
                    Jun 8, 2015 at 21:22
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.