2

I have a settings page on my jQuery mobile website When user clicks save button, I update the server for 3 different user inputs i.e language, currency, threshold

In order to do this I make 3 separate ajax calls (with PUT). So when all are succesfull I go to another page if any of those fails I stay on the same page and show the error messages.

The problem is I only want to switch the page if all calls are succesful, and if there are any errors I want to show one alert with all messages (rather than 3 separete alert windows), so I need to wait the results of all these calls.

To achive this in all 3 Ajax calls I used;

async:false 

And I put a boolean in all these calls succes methods like;

     success: function (data){
           languageUpatesuccesful=true;

        }

and then something like this;

 if(languageUpatesuccesful){
   make the next call to update currency..etc
}

...

if(allsuccesful(){
  changepage();
}

So I can track when exactly when one calls finishes then I make the next call, if all succesful switch to another page.

While this works, I think this is a horrible solution, is there a way to achive this by using async:true ?

Because disabling asynchrous ajac freezes the page and I cant even show animations, also it is not recommended by jQuery. But then how can I know when these 3 calls are finished and take action depending on result?

AJ.
  • 16,368
  • 20
  • 95
  • 150
Spring
  • 11,333
  • 29
  • 116
  • 185
  • 2
    Why not send all of your data with just one AJAX call? – marekful Jan 29 '13 at 15:54
  • @Marcell Fülöp how? web service have 3 different update methods – Spring Jan 29 '13 at 15:55
  • Send them all in one ajax, and don't use `async: false`. There is always a better way to do it than to use `async: false`. Turning off async sets you up for failure with the UI. – Jonathan M Jan 29 '13 at 15:55
  • There are different ways to know when all of your multiple AJAX calls returned. The simpler way is to use a timer based check mechanism. Each of you success callbacks could set some variables which are examined in the timer based function. Another way is to implement your own event/listener model. – marekful Jan 29 '13 at 15:56
  • @Jonathan M how can i use only one ajax? while there 3 different update web service URLS? – Spring Jan 29 '13 at 15:56
  • @Marcell Fülöp can you explain what u mean by timer? – Spring Jan 29 '13 at 15:57
  • @Spring, have each ajax call update a global array that shows when all three have completed. If, after updating the array, the array shows all three have completed, do your processing. – Jonathan M Jan 29 '13 at 15:58
  • @Spring obviously for one AJAX, you need server side changes but it should be relatively easy to change your backend to save the data arriving in one request to different places... – marekful Jan 29 '13 at 15:58

3 Answers3

8

Use deferred objects together with $.when:

$.when(ajax1(), ajax2(), ajax3()).done(function() {
    // all Ajax calls successful, yay! 
}).fail(function() {
    // oh noes, at least one call failed!
});

Where ajaxX is defined as:

function ajax1() {
    return $.ajax(...);
}

If you indeed want to chain the Ajax calls instead of having them concurrent, have a look at Bergi's solution.

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • @Felix Kling tnx can u give an example what is ajax1() looks like? it is just a function including an ajax call? – Spring Jan 29 '13 at 16:01
  • @Felix Kling should I remove all succes and error handlers from inside the ajax1() fucntions? – Spring Jan 29 '13 at 16:01
  • I already did that.... any Ajax method in jQuery returns jqXHR object which implement the promise interface. Just pass those values to `$.when`. Regarding the success/error handlers: Depends on what they are doing. If you have to process each response individually, keep them. – Felix Kling Jan 29 '13 at 16:02
  • @Felix Kling tnx but what is $.ajax({…} in that solution? – Spring Jan 29 '13 at 16:06
  • 1
    @Spring: It's jQuery's `ajax` method: http://api.jquery.com/jQuery.ajax/. Maybe you are using `.get` or `.post` or `.getJSON`, but they all call `jQuery.ajax` internally. It does not matter which one you use, I just used `$.ajax` because it is the most generic one. – Felix Kling Jan 29 '13 at 16:09
  • @Felix Kling in your solution can I get the related returning error messages and show them together? – Spring Jan 29 '13 at 16:17
  • @Spring: No, not really. You would only get the error message of the call that failed. If other calls fail too, you would not be able to get those. You should read the `$.when` documentation, it has some suggestion about how to handle multiple Ajax calls in this case: *"If you need to perform additional processing for this case, such as canceling any unfinished ajax requests, you can keep references to the underlying jqXHR objects in a closure and inspect/cancel them in the failCallback."* – Felix Kling Jan 29 '13 at 16:19
  • @FelixKling Is it possible to pass a dynamic number of deferred objects into the `$.when()` call? – Matthew Herbst Nov 25 '14 at 01:04
4

You can easily chain them by using the Deferred interface:

$.ajax({…})
  .then(function(languageUpateResult) {
     return $.ajax({…});
  })
  .then(function(currencyUpdateResult) {
     return $.ajax({…});
  })
  .then(function(thresholdUpdateResult) {
     changePage();
  });

Sorry, I skipped the fact that the ajax calls are separate. The above code executes them sequentially, if you just want to execute them in parallel and wait for all of them to finish use $.when() - see @FelixKling's answer.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • also can you explain what is $.ajax({…}? – Spring Jan 29 '13 at 16:07
  • `$.ajax` is just the jQuery ajax function with its options object. – Bergi Jan 29 '13 at 16:52
  • tnx this is what I need, but couldnt figure out how can I use this. first of all what are those boolean params why do I still need them? – Spring Jan 29 '13 at 17:14
  • Those function arguments are the results of the previous ajax call. Not sure what you need exactly. It doesn't seem you will need booleans, better I rename them. – Bergi Jan 29 '13 at 18:32
  • tnx, could you show in your example how/why you use those arguments? – Spring Jan 29 '13 at 19:21
  • I neither know what your server returns as response nor whether or for what you need it. Maybe you can omit them entirely. But as you want to make the requests sequentially, I'd expected that you needed the success result of the previous one. – Bergi Jan 29 '13 at 19:27
  • could you also look at this one tnx http://stackoverflow.com/questions/14609080/jquery-mobile-how-to-detect-refresh – Spring Jan 30 '13 at 20:18
1

You can try using web workers to do your async calls in a background thread. Web workers have pretty good browser support and should solve your issue. If you are interested, I have written a little abstraction of the web worker library to make them easier to work with (just send me a pm).

mako-taco
  • 722
  • 5
  • 10