How do I chain three asynchronous calls using jQuery promises?
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.
John Mcdock
Updated on February 19, 2020Comments
-
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 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 about 11 yearsHow do I chain multiple ajax calls and pass the return value? deferred.resolve used to be the way I used to do it.
-
SLaks about 11 years@JohnMcdock: Return a value from
.then()
(in 1.8+). See api.jquery.com/deferred.then -
Jerinaw over 10 yearsThis 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 over 10 yearsNo 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 about 10 yearsOne 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 about 10 yearsjust checked with jquery 1.11.0 and all then calls get arguments from when and not from previous then.
-
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 about 10 years@Emilio, yes precisely. That is what the OP asked for and this answer does not deliver in that regard.
-
pajics about 10 yearsUnfortunately 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 about 10 yearsTo 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 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 almost 10 yearsDV: doesn't answer the question - misleading
-
Daniel Buckmaster over 9 yearsNot the answer the OP was looking for, but the one I was. Thanks!
-
Derek Henderson about 9 yearsAnswers a completely different question. Recommend removing in order to avoid confusion.
-
jchook about 9 yearsThe three ajax calls in this example will happen asynchronously
-
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 about 8 yearsWhen you call a
notify()
method on aDeferred
object, you pass an object to it. Then anyone who subscribed to progress updates of thatDeferred
'spromise
will be called with that object. -
Arturo Torres Sánchez over 7 yearsThis answer doesn't chain the requests, but are rather run in parallel.
-
vsync almost 6 yearsWhat 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-callsecond
until{state:"ready"}
and only then you can continue tothird
-
ilian6806 over 5 yearsIt worked for me ! I only needed to change function next() to function next(ret), becaouse ret was not defined :)
-
MarSoft over 5 yearsThanks @ilian6806, fixed.
-
Nick Johnson about 4 yearsBy 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 almost 3 yearsNote 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).