758

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?

Maistrenko Vitalii
  • 994
  • 1
  • 8
  • 16
lejahmie
  • 17,938
  • 16
  • 54
  • 77
  • How are you calling your original ajax requests? – NakedBrunch Sep 14 '10 at 14:07
  • 2
    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 http://api.jquery.com/category/deferred-object/ – Adriano Sep 11 '14 at 08:50

22 Answers22

1023

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.

Aliaksandr Sushkevich
  • 11,550
  • 7
  • 37
  • 44
Alex
  • 18,332
  • 10
  • 49
  • 53
  • 54
    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 08 '12 at 08:24
  • 2
    Is it possible to bunch the requests ajax1..4 into an array and pass that? – andig May 05 '13 at 09:43
  • @andig See [linked question](http://stackoverflow.com/questions/9865586/jquery-when-troubleshooting-with-variable-number-of-arguments)... it's a little complicated but can be done – Alex May 06 '13 at 04:31
  • 41
    Be careful with the `fail` case. Unlike `done`, `fail` fires immediately on the first fail and disregards the remaining deferreds. – Ryan Mohr Jul 02 '13 at 00:08
  • 1
    @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 http://www.html5rocks.com/en/tutorials/es6/promises/ – Adriano Sep 11 '14 at 08:57
  • 2
    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 '14 at 22:46
  • (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.) – Ziggy Dec 22 '14 at 23:00
  • i tryed it and in my console log it do two ajax call. i mean, it double all ajax call – Gino Jan 20 '15 at 22:12
  • This is great, but how can it be done dynamically (ie. in a loop). Can the function receive an array of functions? – Victor Mota Sep 16 '15 at 16:18
  • 1
    @VictorMota Yes, you need to use 'apply' - see the last couple of paragraphs in the answer text. – Alex Sep 17 '15 at 00:39
  • Just curious... how was this done before promises and the new .when() ? – carinlynchin Aug 30 '17 at 18:41
  • 1
    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. – Legends Jan 27 '18 at 17:45
  • 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 – tonyduanesmith Mar 02 '18 at 13:23
  • hey, buddy! can you add example with Promise? I can't understand how to resolve when success or failed =( – Georgiy Chebotarev Oct 19 '18 at 09:25
  • how to get data from this? what a hell, how this work) just a riddle – Georgiy Chebotarev Oct 19 '18 at 09:37
  • what if i have my requests inside a loop and they depend on the number of iterations how would this help me in that case – Muhammad Omer Aslam Nov 14 '18 at 02:09
  • how can I get some simple response like: var sourceCode = someFunctionWithAsyncAjax('www.someurl.com/filetext.html'); dosometingWithText(sourceCode); – Rubén Ruíz Sep 19 '19 at 22:13
  • @RyanMohr use `Promise.allSettled([$.ajax(), $.ajax()]).then()` to wait until _all_ are resolved – João Pimentel Ferreira Apr 28 '21 at 19:43
333

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) { }
Arsen Khachaturyan
  • 7,904
  • 4
  • 42
  • 42
  • 19
    +1 Much better than other answers ***in case*** you have to deal with 3rd party scripts with anonymous callbacks/closures. – kaiser Sep 30 '13 at 13:50
  • 5
    @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 '13 at 23:24
  • 7
    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 '14 at 13:06
  • 6
    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 '14 at 12:20
  • @ArsenKhachaturyan 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 http://api.jquery.com/category/ajax/global-ajax-event-handlers/ – Adriano Sep 11 '14 at 08:44
  • 4
    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 http://jsfiddle.net/36votxba/2/ – Arsen Khachaturyan Sep 16 '14 at 08:40
  • 1
    If you call another loop of AJAX requests using this you get an endless loop. Oops. :) – Wes Mar 10 '17 at 15:09
  • 1
    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. – Arsen Khachaturyan Mar 21 '17 at 20:50
  • 1
    THANK YOU, first solution to multiple ajax calls that actually worked out of a dozen different solutions. – Edward B. Aug 05 '19 at 20:53
  • 1
    This seems like the best solution except that it binds the event to the document and it fires then everytime all ajax finish. So if this is undesired, do unbind it in the callback using $(document).off('ajaxStop'); – Daniel Katz Feb 15 '20 at 16:40
  • does the first example `$(document).ajaxStop()` work for xmlHttpRequest ajax method? – user3050478 Sep 11 '21 at 03:17
  • Frankly never tried it on xmlHttpRequest, however, according to documentation jQuery can deal with such requests. – Arsen Khachaturyan Sep 17 '21 at 09:04
33

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);
        }
    });
Community
  • 1
  • 1
lejahmie
  • 17,938
  • 16
  • 54
  • 77
  • 38
    It looks like you've forgotten to give proper attribution to [this answer](http://stackoverflow.com/questions/3034874/sequencing-ajax-requests/3035268#3035268), I've added it. – Tim Post Feb 09 '11 at 15:15
22

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();
    });
});
BBonifield
  • 4,983
  • 19
  • 36
  • 11
    This seems like it would overly complicate a trivial problem. – Chris Sep 14 '10 at 14:29
  • 2
    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 '10 at 15:00
  • Thanks @BBonifield for the answer - I wrote a utility function based on your answer. Refer: http://stackoverflow.com/questions/3709597/wait-until-all-jquery-ajax-request-are-done/9350515#9350515 – Sanjeev Kumar Dangi Feb 19 '12 at 15:41
  • 1
    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 '15 at 04:30
  • 1
    @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 '15 at 18:00
  • 2
    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 '15 at 09:32
  • why do we need `var semaphore` ? Isn't enough to have only `all_queued` ? – Disappointed Dec 02 '15 at 13:55
  • 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). – Ofir Jul 10 '16 at 13:34
  • The answer is almost 6 years old. jQuery didn't have `$.when()` when this was written. – BBonifield Jul 10 '16 at 17:21
21

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.

Igor Ivancha
  • 3,413
  • 4
  • 30
  • 39
olafure
  • 3,518
  • 2
  • 33
  • 45
  • 5
    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 '13 at 23:27
  • As of jQuery 1.8, the .ajaxStop() method should only be attached to document. – Geomorillo Apr 20 '14 at 04:33
  • 1
    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 '15 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 '19 at 18:24
10

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

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

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
});
Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
shmuel613
  • 1,702
  • 1
  • 15
  • 17
  • 45
    One thing to note is that using { async: false } can temporarily lock the browser. http://api.jquery.com/jQuery.ajax/ – BBonifield Sep 14 '10 at 14:53
  • 2
    Exactly! This way you know that at the end of the function which calls all the ajax requests, that they are all finished executing. – shmuel613 Sep 16 '10 at 09:47
  • BBonifield, I am experiencing this behaviour in IE 6. Quite annoying! – Arkiliknam Apr 16 '12 at 13:15
  • 32
    This runs contrary to standard jQuery/Javascript practice. AJAX is always supposed to be asynchronous. You should use jQuery.when() instead. – SystemParadox Apr 24 '12 at 14:50
  • 45
    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 08 '12 at 07:48
  • 6
    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 '12 at 04:34
  • 3
    Unfortunetly they removed { async : false } from 1.8 which is really bad. Sometimes you just need blocking call with optional timeout. – Nickolodeon Jun 15 '12 at 15:02
  • 1
    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. – Cthulhu Jul 24 '12 at 10:07
  • 28
    This is a horribly bad idea. DO NOT USE THIS ANSWER. – Tauren Dec 27 '12 at 23:27
  • 1
    @Learner 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. – Ruan Mendes Oct 23 '13 at 23:26
  • 1
    This is bad for gui usability – Geomorillo Apr 20 '14 at 04:30
  • 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. – Alexis Wilke Jan 12 '15 at 04:28
  • @Nickolodeon, 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. – Frédéric Apr 21 '15 at 12:29
  • 1
    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 https://xhr.spec.whatwg.org/#sync-warning (applies to `async` parameter of xhr `open` method, which is what uses jQuery). – Frédéric Apr 21 '15 at 12:30
  • 1
    Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help http://xhr.spec.whatwg.org/ – Ramesh Kumar Thiyagarajan Jul 27 '16 at 11:02
  • 1
    this is the worse solution from UX perspective but solves the problem ! – Ignacio Vazquez Aug 15 '16 at 20:14
  • 4
    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. – cneeds Oct 31 '17 at 12:51
  • @cneeds Nobody genuinely needs it. There's always a better way to code whatever it is you're doing and make it work asynchronously. – ADyson Sep 18 '19 at 23:13
  • @ADyson please visit chris.chrisjneeds.com/start.html and tell me how you would display the images AFTER they have completely loaded so that the user doesn't have to watch them streaming down the page like ancient scrolls being unravelled. – cneeds Sep 20 '19 at 05:09
  • @cneeds Sorry I'm not seeing your point. In your code, the synchronous XHR is the HEAD request to test if the image exists (which IMO is a bit pointless, you could just try and load the image and wait for it to fail. You're using two HTTP requests where one would do.) But the way you are actually loading the images themselves does not use AJAX and is asynchronous anyway. If you watch your Network tab you'll see the the loading of the actual images still happens in parallel. If you wanted to avoid that (which I think is what you're trying to say), you haven't succeeded. – ADyson Sep 20 '19 at 08:44
  • @cneeds P.S. if you still feel the HEAD is necessary, you can call it asynchronously without a problem - you can simply avoid setting the img.src until the HEAD request returns successfully. Your scenario doesn't demonstrate any requirement at all for synchronous AJAX as far as I can see. If you _did_ want to make your images load synchronously for some reason, you could [wrap the image load event in a Promise](https://jsfiddle.net/fracz/kf8c6t1v/) and then chain a series of promises together. But that would just slow down the loading process. Loading them in parallel is a better UX. – ADyson Sep 20 '19 at 08:47
  • @ADyson with due respect you're missing various points. a) i know my page doesn't work as intended, hence asking you to "fix" it, if you can. b) loading the images in parallel is NOT the requirement. what we'd like to see is no image displayed until ALL of them have loaded. c) the reason for testing if the img does not exist is so that another img can be found to replace it. btw my mail address is chis at chrisjneeds dot com – cneeds Sep 20 '19 at 09:19
  • @cneeds you would be better to open a proper question about this. Then I (and others) can spend time to help you fully. I didn't realise you were aware the page didn't work as intended, I was under the impression from your comment that you were trying to use that code to invalidate my point, as if it was an example of the necessity of synchronous AJAX. But now I understand better, I'm happy to help if you link me to a question here :-) – ADyson Sep 20 '19 at 09:21
  • @ADyson i will do that when i have some time—but i really think my answer is here somewhere. i have tried many of the responses here and will try a few more before i set up a question. such a question will need to be carefully crafted as most readers will see it from an asynch POV and will be reluctant to help with creating synchronous code (i'm tired of being shouted at) :-) – cneeds Sep 20 '19 at 09:26
  • @cneeds Actually here you go, for free, it was simpler than I thought, just using Promises: https://jsfiddle.net/32xgepjc/7/ . The images are loaded in parallel, which saves time, but not displayed until all of them have finished successfully downloading. If an image fails to download, then it is replaced in the "pending" queue by a fresh request to a different URL. The HEAD request is removed (a direct request to an invalid URL will return a 404 anyway so the outcome is the same). The code is also far better encapsulated without all those horrible globals tightly coupling everything. Enjoy. – ADyson Sep 20 '19 at 09:42
  • @cneeds and as I said...no need for anything synchronous. It would just slow the whole thing down. More info on Promise.all is [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). – ADyson Sep 20 '19 at 09:46
  • @ADyson – thank you for the promise info, i will study it now. i never was able to get promise to work. if i and some friends have a website that's not intended for general consumption and we are prepared to wait, then "slow the whole thing down" is not anyone else's concern. ideally, we would like the next set of images to be loading into a hidden div whilst we are looking at the first set. but, since i can't get the first requirement to work, i haven't spent time on this yet... – cneeds Sep 20 '19 at 10:04
  • @cneeds well that's exactly what my code would enable you to do (and without slowing anything down with sequential HTTP requests. Instead, the downloading of each set of images happens in parallel, but the actual displaying of the images is delayed until all of them have downloaded. Note also that there's no AJAX involved). Take a look at the demo link and you'll see. – ADyson Sep 20 '19 at 10:18
  • @ADyson – i will look again, now. maybe i will understand your code better with the intervening two year's experience. i appreciate your feedback... – cneeds Sep 20 '19 at 10:24
  • @cneeds No problem. If you have any further needs around this topic, e.g. extending the functionality or integrating it back into your website, then open a new question about it and ping me a comment with the link :-) – ADyson Sep 20 '19 at 10:29
  • @ADyson – i missed your 32xgepjc fiddle comment. thank you in advance, will check it out now... – cneeds Sep 20 '19 at 10:48
  • @ADyson — i'm implementing a version using your solution but i'm bothered by "those horrible globals tightly coupling everything". i do this so i don't have to concern myself with scope. please explain the better way :-) – cneeds Sep 20 '19 at 11:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/199731/discussion-between-cneeds-and-adyson). – cneeds Sep 20 '19 at 11:55
8

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

Stefano
  • 18,083
  • 13
  • 64
  • 79
4

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="">
</div>
SilverSurfer
  • 4,281
  • 6
  • 24
  • 50
2

If you need something simple; once and done callback

        //multiple ajax calls above
        var callback = function () {
            if ($.active !== 0) {
                setTimeout(callback, '500');
                return;
            }
            //whatever you need to do here
            //...
        };
        callback();
Fatmuemoo
  • 2,187
  • 3
  • 17
  • 34
2

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.

George Mavritsakis
  • 6,829
  • 2
  • 35
  • 42
2

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>
cilf
  • 650
  • 7
  • 16
2

I'm using size check when all ajax load completed

function get_ajax(link, data, callback) {
    $.ajax({
        url: link,
        type: "GET",
        data: data,
        dataType: "json",
        success: function (data, status, jqXHR) {
            callback(jqXHR.status, data)
        },
        error: function (jqXHR, status, err) {
            callback(jqXHR.status, jqXHR);
        },
        complete: function (jqXHR, status) {
        }
    })
}

function run_list_ajax(callback){
    var size=0;
    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>
tranchau
  • 103
  • 7
2

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/

GammaGames
  • 1,617
  • 1
  • 17
  • 32
2

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.
  });
}
Igor Ivancha
  • 3,413
  • 4
  • 30
  • 39
Sanjeev Kumar Dangi
  • 6,925
  • 7
  • 28
  • 30
0

I found simple way, it using shift()

function waitReq(id)
{
  jQuery.ajax(
  {
    type: 'POST',
    url: ajaxurl,
    data:
    {
      "page": id
    },
    success: function(resp)
    {
      ...........
      // 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
uingtea
  • 6,002
  • 2
  • 26
  • 40
0

$.when doesn't work for me, callback(x) instead of return x worked as described here: https://stackoverflow.com/a/13455253/10357604

thestruggleisreal
  • 940
  • 3
  • 10
  • 26
0

Look at my solution:

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
Igor Ivancha
  • 3,413
  • 4
  • 30
  • 39
BCsongor
  • 869
  • 7
  • 11
0

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,
    });
}
Prince Prasad
  • 1,528
  • 1
  • 16
  • 20
0

My solution is as follows

var request;
...
'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

Igor Ivancha
  • 3,413
  • 4
  • 30
  • 39
-2

This is working for me It's very simple

return $.ajax({
  type: 'POST',
  url: urlBaseUrl
  data: {someData:someData},
  dataType: "json",
  success: function(resultData) { 
  }
});
sawan
  • 2,341
  • 3
  • 25
  • 51
  • 1
    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 '21 at 17:41
-3

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;
}
ChinaHelloWorld
  • 1,007
  • 1
  • 12
  • 6
  • 1
    Your `setTimeout()` does NOT `take a sleep`. In this case, you just block all tabs until `done` becomes true. – Alexis Wilke Jan 12 '15 at 04:24
  • 1
    I think that is this topic asking for: "Wait until all jQuery Ajax requests are done". – ChinaHelloWorld Jun 08 '15 at 00:52
  • 1
    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 08 '15 at 21:22