How do I chain three asynchronous calls using jQuery promises?

103,060

Solution 1

In each case, return the jqXHR object returned by $.ajax().

These objects are Promise-compatible so can be chained with .then()/.done()/.fail()/.always().

.then() is the one you want in this case, exactly as in the question.

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

function second(data, textStatus, jqXHR) {
   return $.ajax(...);
}

function third(data, textStatus, jqXHR) {
   return $.ajax(...);
}

function main() {
    first().then(second).then(third);
}

Arguments data, textStatus and jqXHR arise from the $.ajax() call in the previous function, ie. first() feeds second() and second() feeds third().

DEMO (with $.when('foo') to deliver a fulfilled promise, in place of $.ajax(...)).

Solution 2

There is actually a much easier approach when using promises with jQuery. Have a look at the following:

$.when(
    $.ajax("/first/call"),
    $.ajax("/second/call"),
    $.ajax("/third/call")
    )
    .done(function(first_call, second_call, third_call){
        //do something
    })
    .fail(function(){
        //handle errors
    });

Simply chain all your calls into the $.when(...) call and handle the return values in the .done(...) call.

Here's a walkthrough if you prefer: http://collaboradev.com/2014/01/27/understanding-javascript-promises-in-jquery/

Solution 3

Quite late to reply, but I guess answers are missing some straight forward code for chaining. Chaining events is pretty simple with promise support in jquery. I use the following for chaining:

$.ajax()
.then(function(){
   return $.ajax() //second ajax call
})
.then(function(){
   return $.ajax() //third ajax call
})
.done(function(resp){
   //handle final response here
 })

It's simple with no complicated for loops or multiple nested callbacks.

Solution 4

It's much simpler than that.

$.ajax already returns a promise (Deferred object), so you can simply write

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

Solution 5

The best way to do this is by making a reusable function for this. This can even be done with just one line of code using reduce:

function chainPromises(list) {
    return list.reduce((chain, func) => chain ? chain.then(func) : func(), null);
}

This function accepts an array of callbacks which return a promise object, like your three functions.

Example usage:

chainPromises([first, second, third]).then(function (result) {
    console.log('All done! ', result);
});

This way the result of first will also automatically be the parameter of second, so basically what happens is this:

first().then(function(res1) { return second(res1) })
       .then(function(res2) { return third(res2)  })
       .then(function(result) { console.log('All done! ', result) });

And of course you could add as many functions to the array as you want.

Share:
103,060
John Mcdock
Author by

John Mcdock

Updated on February 19, 2020

Comments

  • John Mcdock
    John Mcdock over 4 years

    I have three HTTP calls that need I need to make in a synchronous manner and how do I pass data from one call to the other?

    function first()
    {
       ajax()
    }
    
    function second()
    {
       ajax()
    }
    
    function third()
    {
       ajax()
    }
    
    
    function main()
    {
        first().then(second).then(third)
    }
    

    I tried to use the deferred for the two functions and I came up with a partial solution. Can I extend it to be for three functions?

    function first() {
        var deferred = $.Deferred();
         $.ajax({
    
                 "success": function (resp)
                 {
    
                     deferred.resolve(resp);
                 },
    
             });
        return deferred.promise();
    }
    
    function second(foo) {
         $.ajax({
                "success": function (resp)
                {
                },
                "error": function (resp)
                {
                }
            });
    }
    
    
    first().then(function(foo){second(foo)})
    
  • Mark Pieszak - Trilon.io
    Mark Pieszak - Trilon.io about 11 years
    @SLaks Is it possible to have more than one deferred in the then? As in first().then(second, third).then(fourth); ?
  • John Mcdock
    John Mcdock about 11 years
    How do I chain multiple ajax calls and pass the return value? deferred.resolve used to be the way I used to do it.
  • SLaks
    SLaks about 11 years
    @JohnMcdock: Return a value from .then() (in 1.8+). See api.jquery.com/deferred.then
  • Jerinaw
    Jerinaw over 10 years
    This doesn't work for me. When I try you example the results from first are passed to second AND third. So first feeds second and third. Not first feeds second, second feeds third. $.ajax({...}).then(function(..){ return $.ajax({...}); }).then( function(...){ /* I want the results from the second call. */ });
  • Beetroot-Beetroot
    Beetroot-Beetroot over 10 years
    No time right now to run a test but I can't see why the code in my answer shouldn't work. Make sure you are using jQuery 1.8.3 or above. Earlier versions may well give the symptom you describe.
  • Emilio
    Emilio about 10 years
    One question about this. What if "/first/call" returns an ID that I need in the second call "second/call/id"?? In this case I need to get first the result of the first call, then the second.
  • pajics
    pajics about 10 years
    just checked with jquery 1.11.0 and all then calls get arguments from when and not from previous then.
  • Beetroot-Beetroot
    Beetroot-Beetroot about 10 years
    @pajics, I have added a demo to the answer. Either 1.11.0 has reverted or you are doing something wrong. Can you provide a fiddle?
  • Beetroot-Beetroot
    Beetroot-Beetroot about 10 years
    @Emilio, yes precisely. That is what the OP asked for and this answer does not deliver in that regard.
  • pajics
    pajics about 10 years
    Unfortunately I cant recreate my code with fiddle (using gmaps) but when I tried something simple like this: jsfiddle.net/CbPbw/1 it worked as expected. I'll have to find out what's wrong with my code. Thanks
  • Beetroot-Beetroot
    Beetroot-Beetroot about 10 years
    To make absolutely sure you are using 1.11, alert($.fn.jquery); at the point in your code where you rely on 1.11- ie where you are trying to get your promise chain working.
  • vsync
    vsync about 10 years
    @SLaks - please extend your answer to better explain what is asked, since it seems your answer is a very important one to the nature and basics of jQuery ajax object, it deserves a few more lines which explains how to chain multiple async functions
  • Dead.Rabit
    Dead.Rabit almost 10 years
    DV: doesn't answer the question - misleading
  • Daniel Buckmaster
    Daniel Buckmaster over 9 years
    Not the answer the OP was looking for, but the one I was. Thanks!
  • Derek Henderson
    Derek Henderson about 9 years
    Answers a completely different question. Recommend removing in order to avoid confusion.
  • jchook
    jchook about 9 years
    The three ajax calls in this example will happen asynchronously
  • vsync
    vsync over 8 years
    @Beetroot-Beetroot - where do you place the done method for each promise separately? I would imagine $.when(first()).done(..).then(second).done(...).then(third);
  • MarSoft
    MarSoft about 8 years
    When you call a notify() method on a Deferred object, you pass an object to it. Then anyone who subscribed to progress updates of that Deferred's promise will be called with that object.
  • Arturo Torres Sánchez
    Arturo Torres Sánchez over 7 years
    This answer doesn't chain the requests, but are rather run in parallel.
  • vsync
    vsync almost 6 years
    What if second needs to be re-called until the response is what you expect? for example {state:"running"} and you must use timeout to re-call second until {state:"ready"} and only then you can continue to third
  • ilian6806
    ilian6806 over 5 years
    It worked for me ! I only needed to change function next() to function next(ret), becaouse ret was not defined :)
  • MarSoft
    MarSoft over 5 years
    Thanks @ilian6806, fixed.
  • Nick Johnson
    Nick Johnson about 4 years
    By far the best solution. I picked up the snippet and integrated it into a library requiring groupings of chained Ajax events. Sadly, I had to change the ES6 syntax, but still a clean solution. Well done. I hope more people vote this one up.
  • Dave Cluderay
    Dave Cluderay almost 3 years
    Note that in jquery versions earlier than 1.8, you will need to use .pipe() instead of .then() in order to get the expected chaining behaviour (i.e. where the results from the previous call feed into the next).