27

I use jQuery. And I don't want parallel AJAX calls on my application, each call must wait the previous before starting. How to implement it? There is any helper?

UPDATE If there is any synchronous version of the XMLHttpRequest or jQuery.post I would like to know. But sequential != synchronous, and I would like an asynchronous and sequential solution.

Jader Dias
  • 81,082
  • 147
  • 410
  • 611
  • 4
    You do know what the "A" in AJAX stands for, right :-) – Ed S. Jul 20 '09 at 03:28
  • 2
    This definitely isn't an answer, but it's an interesting sign of a well-designed framework when *this* is the confusing question instead of how to make it concurrent. :) – Sam Harwell Jul 20 '09 at 03:29
  • 8
    @Ed Swangren, yeah but AJAX doesn't have to be async, JavaScript, or XML. :-) – Nosredna Jul 20 '09 at 03:30
  • @280Z28, thank goodness. I remember back when I had my Amiga, DOS and Mac users were always telling me people didn't need multitasking. – Nosredna Jul 20 '09 at 03:34
  • @Ed I understand that A is for Asynchronous. But every Async method can have a Synchronous version. I don't know if for AJAX this synchronous version is already implemented somewhere. – Jader Dias Jul 20 '09 at 03:34
  • 1
    @Nosredna But how could AJAX not be Javascript? – Jader Dias Jul 20 '09 at 04:12

11 Answers11

19

There's a much better way to do this than using synchronous ajax calls. Jquery ajax returns a deferred so you can just use pipe chaining to make sure that each ajax call finishes before the next runs. Here's a working example with a more in depth example you can play with on jsfiddle.

// How to force async functions to execute sequentially 
// by using deferred pipe chaining.

// The master deferred.
var dfd = $.Deferred(),  // Master deferred
    dfdNext = dfd; // Next deferred in the chain
    x = 0, // Loop index
    values = [], 

    // Simulates $.ajax, but with predictable behaviour.
    // You only need to understand that higher 'value' param 
    // will finish earlier.
    simulateAjax = function (value) {
        var dfdAjax = $.Deferred();

        setTimeout(
            function () {
                dfdAjax.resolve(value);
            },
            1000 - (value * 100)
        );

        return dfdAjax.promise();
    },

    // This would be a user function that makes an ajax request.
    // In normal code you'd be using $.ajax instead of simulateAjax.
    requestAjax = function (value) {
        return simulateAjax(value);
    };

// Start the pipe chain.  You should be able to do 
// this anywhere in the program, even
// at the end,and it should still give the same results.
dfd.resolve();

// Deferred pipe chaining.
// What you want to note here is that an new 
// ajax call will not start until the previous
// ajax call is completely finished.
for (x = 1; x <= 4; x++) {

    values.push(x);

    dfdNext = dfdNext.pipe(function () {
        var value = values.shift();
        return requestAjax(value).
            done(function(response) {
                // Process the response here.

            });

    });

}

Some people have commented they have no clue what the code does. In order to understand it, you first need to understand javascript promises. I am pretty sure promises are soon to be a native javascript language feature, so that should give you a good incentive to learn.

Todd Chaffee
  • 6,258
  • 28
  • 39
  • Hey, this is a bit late but anyways: Great answer helped me out and works like a charm. But I really don't get how it works. I tried to understand and read the docs, but this is a mystery to me. – dan-lee Jun 01 '13 at 00:55
  • 1
    @DanLee Looks like you are not the only one. – MD. Sahib Bin Mahboob May 21 '14 at 10:43
  • Hard to understand javascript promises and the way jquery implements the concept without learning it ;-) Plenty of docs out there. – Todd Chaffee May 21 '14 at 16:57
  • I have edited the answer to make it clearer what you need as a background for understanding the code. – Todd Chaffee May 21 '14 at 17:29
  • You're essentially disabling the asynchronous nature of an ajax request by using this pattern, which can similarly be accomplished by simply setting ```async: false``` as an argument to your request. Doing so elicits the exact same behavior as what you are creating with your suggestion. – kamelkev Jun 18 '16 at 00:44
  • 2
    @kamelkev, that's not completely correct. Setting `async: false` will prevent other events in the code from firing while waiting for the request to return. That's highly discouraged because even browser events like clicks will not be triggered. The code I provided allows the aysnc requests to still run async, but in strict sequential order. – Todd Chaffee Nov 14 '16 at 22:17
6

You have two choices that I can think of. One is to chain them through callbacks. The other is to make the calls synchronous rather than async.

Is there a reason you want them sequential? That will slow things down.

To make the call synchronous, you'll set the async option in the Ajax call to false. See the documentation at http://docs.jquery.com/Ajax/jQuery.ajax#options (click options tab to see them).

Nosredna
  • 74,873
  • 15
  • 91
  • 122
2

The best way you could do this is by chaining callbacks as Nosredna said. I wouldn't recommend using synchronous XMLHttpRequest as they lock your entire application.

There aren't much helper for this as far as I know, but you could do something resembling a callback FIFO.

Gab Royer
  • 8,876
  • 7
  • 36
  • 58
2

You could give narrative javascript a try http://www.neilmix.com/narrativejs/doc/

I've never used it myself though. If I wanted to do this, I would setup some kind of abstraction for chaining asynchronous actions. As others have said, the synchonous version of the ajax object blocks events from being processed while it's waiting for a response. This causes the browser to look like it's frozen until it recieves a response.

Breton
  • 14,634
  • 3
  • 56
  • 75
  • Thanks for the addition, but I am not sure if it addresses my problem. It makes async calls sequential if there was only one generating thread, but in my case many events spawns requisitions. – Jader Dias Jul 20 '09 at 03:57
  • Like I said, I haven't used NJS, but its description says that it adds a yield operator that simply stops execution of a function until an event has fired. It doesn't seem to me like that would prevent concurrency. If it did, it wouldn't be a very useful library to anyone. – Breton Jul 20 '09 at 04:01
  • If nothing else, you could study the compiled code and you may find a plain JS solution to your problem that way. – Breton Jul 20 '09 at 04:03
1

Look at this: http://docs.jquery.com/Ajax/jQuery.ajax (click on the "options" tab).

But remember a synchronous call will freeze the page until the response is received, so it can't be used in a production site, because users will get mad if for any reason they have to wait 30 seconds with their browser frozen.

EDIT: ok, with your update it's clearer what you want to achieve ;)

So, your code may look like this:

    $.getJSON("http://example.com/jsoncall", function(data) {
        process(data);
        $.getJSON("http://example.com/jsoncall2", function (data) {
            processAgain(data);
            $.getJSON("http://example.com/anotherjsoncall", function(data) {
                processAgainAndAgain(data);
            });
        });
    });

This way, the second call will only be issued when the response to the first call has been received and processed, and the third call will only be issued when the response to the second call has been received and processed. This code is for getJSON but it can be adapted to $.ajax.

FWH
  • 3,057
  • 1
  • 18
  • 17
  • I am aware of your solution, but the multiple request aren't generated by the same even, so I can't chain them together as you showed. My solution would be more like a lock(globalAjaxObject) in C# – Jader Dias Jul 20 '09 at 04:14
1

Set the async option to false, e.g.,

$.ajax({ async: false /*, your_other_ajax_options_here */ });

Reference: Ajax/jQuery.ajax

Joe Chung
  • 11,239
  • 1
  • 19
  • 32
  • 1
    The async option will be removed from jQuery soon. – Mr. Lance E Sloan Dec 04 '12 at 20:13
  • This is the best answer for OP's original request. This suggestion guarantees IOE (in-order-execution) with the least complexity. There have never been plans to remove the async flag from jquery, contrary to what @LS implies. – kamelkev Jun 18 '16 at 00:49
  • 2
    Remember: "As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is deprecated; you must use the success/error/complete callback options instead of the corresponding methods of the jqXHR object such as jqXHR.done()." http://docs.jquery.com/Ajax/jQuery.ajax#options – Mr. Lance E Sloan Jun 18 '16 at 15:05
  • @LS Deprecated does not mean remove. You can read more about it on the original ticket where this was discussed: https://bugs.jquery.com/ticket/11013 – kamelkev Jun 18 '16 at 22:46
1
(async () => { 
  for(f of ['1.json','2.json','3.json']){
    var json = await $.getJSON(f);
    console.log(json)
 };
})()
  1. requests 3 json files with jQuery ajax calls
  2. process in sequence (not in parallel) with await
  3. works in Chrome/Firefox/Edge (as of 1/30/2018)

more at MDN

Rm558
  • 3,263
  • 3
  • 29
  • 37
1

You can use promise to make ajax calls sequential. Using Array push and pop promise method, sequential ajax calls will be lot easier.

var promises = [Promise.resolve()];

function methodThatReturnsAPromise(id) {
  return new Promise((resolve, reject) => {
    $.ajax({
      url: 'https://jsonplaceholder.typicode.com/todos/'+id,
      dataType:'json',
      success: function(data)
      {
        console.log("Ajax Request Id"+id);
        console.log(data);
        resolve();
      }
    });
  });
} 

function pushPromise(id)
{
  promises.push(promises.pop().then(function(){
    return methodThatReturnsAPromise(id)}));
}


pushPromise(1);
pushPromise(3);
pushPromise(2);
Muthu Kumar
  • 151
  • 1
  • 5
-1

Synchronous calls aren't necessarily slower, if you have an app where AJAX calls open, posts to, then closes a socket, multiple calls to the socket don't make sense as some sockets can only handle a single connection, in which case, queuing data so its only sent when the previous AJAX call has completed means much higher data throughput.

tomski777
  • 1
  • 3
-2

How about using Node.js events?

var EventEmitter = require('events').EventEmitter;
var eventEmitter = new EventEmitter();
var $ = require('jquery');

var doSomething = function (responseData) {
  var nextRequestData = {};
  // do something with responseData
  return nextRequestData;
};

// ajax requests
var request1 = $.ajax;
var request2 = $.ajax;
var requests = [request1, request2];

eventEmitter.on('next', function (i, requestData) {
  requests[i](requestData).then(
    function (responseData) {
      console.log(i, 'request completed');
      if (i+1 < requests.length) {
        var nextRequestData = doSomething(responseData);
        eventEmitter.emit('next', i+1, nextRequestData);
      }
      else {
        console.log('completed all requests');
      }
    },
    function () {
      console.log(i, 'request failed');
    }
  );
});

var data = {
  //data to send with request 1
};
eventEmitter.emit('next', 0, data);
tsknakamura
  • 148
  • 3
-3

sequential != synchronous, and I would like an asynchronous and sequential solution

Synchronous execution generally means "using the same clock", while sequential execution means "following in order or sequence".

For your specific use case I think both conditions must be met, as asynchronous execution implies the possibility of a non-sequential result.

kamelkev
  • 1,098
  • 10
  • 24