[jquery] jQuery callback for multiple ajax calls

I want to make three ajax calls in a click event. Each ajax call does a distinct operation and returns back data that is needed for a final callback. The calls themselves are not dependent on one another, they can all go at the same time, however I would like to have a final callback when all three are complete.

$('#button').click(function() {
    fun1();
    fun2();
    fun3();
//now do something else when the requests have done their 'success' callbacks.
});

var fun1= (function() {
    $.ajax({/*code*/});
});
var fun2 = (function() {
    $.ajax({/*code*/});
});
var fun3 = (function() {
    $.ajax({/*code*/});
});

This question is related to jquery ajax callback

The answer is


It's worth noting that since $.when expects all of the ajax requests as sequential arguments (not an array) you'll commonly see $.when used with .apply() like so:

// Save all requests in an array of jqXHR objects
var requests = arrayOfThings.map(function(thing) {
    return $.ajax({
        method: 'GET',
        url: 'thing/' + thing.id
    });
});

$.when.apply(this, requests).then(function(resp1, resp2/*, ... */) {
    // Each argument is an array with the following structure: [ data, statusText, jqXHR ]
    var responseArgsArray = Array.prototype.slice.call(this, arguments);

});

Using the Spread syntax, you can now write this code like so:

$.when(...requests).then((...responses) => {
    // do something with responses
})

This is because $.when accepts args like this

$.when(ajaxRequest1, ajaxRequest2, ajaxRequest3);

And not like this:

$.when([ajaxRequest1, ajaxRequest2, ajaxRequest3]);

I like hvgotcodes' idea. My suggestion is to add a generic incrementer that compares the number complete to the number needed and then runs the final callback. This could be built into the final callback.

var sync = {
 callbacksToComplete = 3,
 callbacksCompleted = 0,
 addCallbackInstance = function(){
  this.callbacksCompleted++;
  if(callbacksCompleted == callbacksToComplete) {
   doFinalCallBack();
  }
 }
};

[Edited to reflect name updates.]


Ok, this is old but please let me contribute my solution :)

function sync( callback ){
    syncCount--;
    if ( syncCount < 1 ) callback();
}
function allFinished(){ .............. }

window.syncCount = 2;

$.ajax({
    url: 'url',
    success: function(data) {
        sync( allFinished );
    }
});
someFunctionWithCallback( function(){ sync( allFinished ); } )

It works also with functions that have a callback. You set the syncCount and you call the function sync(...) in the callback of every action.


It's not jquery (and it appears jquery has a workable solution) but just as another option....

I've had similar problems working heavily with SharePoint web services - you often need to pull data from multiple sources to generate input for a single process.

To solve it I embedded this kind of functionality into my AJAX abstraction library. You can easily define a request which will trigger a set of handlers when complete. However each request can be defined with multiple http calls. Here's the component (and detailed documentation):

DPAJAX at DepressedPress.com

This simple example creates one request with three calls and then passes that information, in the call order, to a single handler:

    // The handler function 
function AddUp(Nums) { alert(Nums[1] + Nums[2] + Nums[3]) }; 

    // Create the pool 
myPool = DP_AJAX.createPool(); 

    // Create the request 
myRequest = DP_AJAX.createRequest(AddUp); 

    // Add the calls to the request 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [5,10]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [4,6]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [7,13]); 

    // Add the request to the pool 
myPool.addRequest(myRequest); 

Note that unlike many of the other solutions (including, I believe the "when" solution in jquery) provided this method does not force single threading of the calls being made - each will still run as quickly (or as slowly) as the environment allows but the single handler will only be called when all are complete. It also supports the setting of timeout values and retry attempts if your service is a little flakey.

I've found it insanely useful (and incredibly simple to understand from a code perspective). No more chaining, no more counting calls and saving output. Just "set it and forget it".


Looks like you've got some answers to this, however I think there is something worth mentioning here that will greatly simplify your code. jQuery introduced the $.when in v1.5. It looks like:

$.when($.ajax(...), $.ajax(...)).then(function (resp1, resp2) {
    //this callback will be fired once all ajax calls have finished.
});

Didn't see it mentioned here, hope it helps.


I asked the same question a while ago and got a couple of good answers here: Best way to add a 'callback' after a series of asynchronous XHR calls


I found an easier way to do it without the need of extra methods that arrange a queue.

JS

$.ajax({
  type: 'POST',
  url: 'ajax1.php',
  data:{
    id: 1,
    cb:'method1'//declaration of callback method of ajax1.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method1'
  eval(cb+"(JSON.stringify(data));");//here we calling method1 and pass all data
  }
});


$.ajax({
  type: 'POST',
  url: 'ajax2.php',
  data:{
    id: 2,
    cb:'method2'//declaration of callback method of ajax2.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method2'
  eval(cb+"(JSON.stringify(data));");//here we calling method2 and pass all data
  }
});


//the callback methods
function method1(data){
//here we have our data from ajax1.php
alert("method1 called with data="+data);
//doing stuff we would only do in method1
//..
}

function method2(data){
//here we have our data from ajax2.php
alert("method2 called with data="+data);
//doing stuff we would only do in method2
//..
}

PHP (ajax1.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method1'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax1"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

PHP (ajax2.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method2'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax2"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

I got some good hints from the answers on this page. I adapted it a bit for my use and thought I could share.

// lets say we have 2 ajax functions that needs to be "synchronized". 
// In other words, we want to know when both are completed.
function foo1(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo1');
            callback();               
        }
    });
}

function foo2(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo2');
            callback();
        }
    });
}

// here is my simplified solution
ajaxSynchronizer = function() {
    var funcs = [];
    var funcsCompleted = 0;
    var callback;

    this.add = function(f) {
        funcs.push(f);
    }

    this.synchronizer = function() {
        funcsCompleted++;
        if (funcsCompleted == funcs.length) {
            callback.call(this);
        }
    }

    this.callWhenFinished = function(cb) {
        callback = cb;
        for (var i = 0; i < funcs.length; i++) {
            funcs[i].call(this, this.synchronizer);
        }
    }
}

// this is the function that is called when both ajax calls are completed.
afterFunction = function() {
    alert('All done!');
}

// this is how you set it up
var synchronizer = new ajaxSynchronizer();
synchronizer.add(foo1);
synchronizer.add(foo2);
synchronizer.callWhenFinished(afterFunction);

There are some limitations here, but for my case it was ok. I also found that for more advanced stuff it there is also a AOP plugin (for jQuery) that might be useful: http://code.google.com/p/jquery-aop/


$.ajax({type:'POST', url:'www.naver.com', dataType:'text', async:false,
    complete:function(xhr, textStatus){},
    error:function(xhr, textStatus){},
    success:function( data ){
        $.ajax({type:'POST', 
            ....
            ....
            success:function(data){
                $.ajax({type:'POST',
                    ....
                    ....
            }
        }
    });

I'm sorry but I can't explain what I worte cuz I'm a Korean who can't speak even a word in english. but I think you can easily understand it.


EDIT -- perhaps the best option would be to create a service endpoint that does everything the three requests do. That way you only have to do one request, and all the data is where you need it to be in the response. If you find you are doing the same 3 requests over and over again, you will probably want to go this route. It is often a good design decision to set up a facade service on the server that lumps commonly used smaller server actions together. Just an idea.


one way to do it would be to create a 'sync' object in your click handler before the ajax calls. Something like

var sync = {
   count: 0
}

The sync will be bound to the scope of the success calls automatically (closure). In the success handler, you increment the count, and if it is 3 you can call the other function.

Alternatively, you could do something like

var sync = {
   success1Complete: false,
   ...
   success3Complete: false,
}

when each success is executed, it would change the value in the sync to true. You would have to check the sync to make sure that all three are true before proceeding.

Note the case where one of your xhrs does not return success -- you need to account for that.

Yet another option would be to always call the final function in your success handlers, and have it access the sync option to determine whether to actually do anything. You would need to make sure the sync is in the scope of that function though.


Not seeing the need for any object malarky myself. Simple have a variable which is an integer. When you start a request, increment the number. When one completes, decrement it. When it's zero, there are no requests in progress, so you're done.

$('#button').click(function() {
    var inProgress = 0;

    function handleBefore() {
        inProgress++;
    };

    function handleComplete() {
        if (!--inProgress) {
            // do what's in here when all requests have completed.
        }
    };

    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
});

async   : false,

By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false. Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation. Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active. 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() or the deprecated jqXHR.success().


I came across this problem today, and this was my naive attempt before watching the accepted answer.

<script>
    function main() {
        var a, b, c
        var one = function() {
            if ( a != undefined  && b != undefined && c != undefined ) {
                alert("Ok")
            } else {
                alert( "¬¬ ")
            }
        }

        fakeAjaxCall( function() {
            a = "two"
            one()
        } )
        fakeAjaxCall( function() {
            b = "three"
            one()
        } )
        fakeAjaxCall( function() {
            c = "four"
            one()
        } )
    }
    function fakeAjaxCall( a ) {
        a()
    }
    main()
</script>

Examples related to jquery

How to make a variable accessible outside a function? Jquery assiging class to th in a table Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Getting all files in directory with ajax Bootstrap 4 multiselect dropdown Cross-Origin Read Blocking (CORB) bootstrap 4 file input doesn't show the file name Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource how to remove json object key and value.?

Examples related to ajax

Getting all files in directory with ajax Cross-Origin Read Blocking (CORB) Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource Fetch API request timeout? How do I post form data with fetch api? Ajax LARAVEL 419 POST error Laravel 5.5 ajax call 419 (unknown status) How to allow CORS in react.js? Angular 2: How to access an HTTP response body? How to post a file from a form with Axios

Examples related to callback

When to use React setState callback How to send an HTTP request with a header parameter? javascript function wait until another function to finish What is the purpose of willSet and didSet in Swift? How to refactor Node.js code that uses fs.readFileSync() into using fs.readFile()? Aren't promises just callbacks? How do I convert an existing callback API to promises? How to access the correct `this` inside a callback? nodeJs callbacks simple example Callback after all asynchronous forEach callbacks are completed