How do I chain three asynchronous calls using jQuery promises? – Even if we have a good project plan and a logical concept, we will spend the majority of our time correcting errors abaout javascript and jquery. Furthermore, our application can run without obvious errors with JavaScript, we must use various ways to ensure that everything is operating properly. In general, there are two types of errors that you’ll encounter while doing something wrong in code: Syntax Errors and Logic Errors. To make bug fixing easier, every JavaScript error is captured with a full stack trace and the specific line of source code marked. To assist you in resolving the JavaScript error, look at the discuss below to fix problem about How do I chain three asynchronous calls using jQuery promises?.
Problem :
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)})
Solution :
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(...)
).
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/
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.
It’s much simpler than that.
$.ajax
already returns a promise (Deferred object), so you can simply write
function first() {
return $.ajax(...);
}
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.
You can write it in more functional manner:
[function() { return ajax(...)}, function(data) { return ajax(...)}]
.reduce(function(chain, callback) {
if(chain) {
return chain.then(function(data) { return callback(data); });
} else {
return callback();
}
}, null)
I found a good looking solution here: How do I chain a sequence of deferred functions in jQuery 1.8.x?
And here is my own implementation of similar approach, somewhat ugly but probably working. It broadcasts result of each method as a «progress update» on returned promise object.
$.chain = function() {
var defer = $.Deferred();
var funcs = arguments;
var left = funcs.length;
function next(lastResult) {
if(left == 0) {
defer.resolve();
return;
}
var func = funcs[funcs.length - left]; // current func
var prom = func(lastResult).promise(); // for promise will return itself,
// for jquery ojbect will return promise.
// these handlers will be launched in order we specify them
prom.always(function() {
left--;
}).done(function(ret) {
defer.notify({
idx: funcs.length-left,
left: left,
result: ret,
success: true,
});
}).fail(function(ret) {
defer.notify({
idx: funcs.length-left,
left: left,
result: ret,
success: false,
});
}).always(function(ret) {
next(ret);
});
}
next();
return defer.promise();
};
How to use it for your situation? Maybe not beautiful, but it should work:
function first() {
return ajax(...);
}
var id;
funciton second() {
return ajax(id, ...);
}
function third() {
return ajax(id, ...);
}
$.chain(first, second, third).progress(function(p) {
if(p.func == first)
id = p.result.identifier;
}).then(function() {
alert('everything is done');
});
Or you can just assign that id variable from first
function.
Or if you only need previous function’s result, you can use this approach:
function first() {
return ajax(...);
}
function second(first_ret) {
return ajax(first_ret.id, ...);
}
function third(second_ret) {
return ajax(second_ret.something, ...);
}
The following appears to work and allows the list of functions to be dynamic:
<html>
<head>
<title>demo chained synchronous calls</title>
</head>
<body>
<script src="http://code.jquery.com/jquery-2.2.4.min.js"></script>
<script type="text/javascript">
function one(parms) {
console.log('func one ' + parms);
return 1;
}
function two(parms) {
console.log('func two ' + parms);
return 2;
}
function three(parms) {
console.log('func three ' + parms);
return 3;
}
function four(parms) {
console.log('func four ' + parms);
return 4;
}
var funcs = ['one', 'two', 'three', 'four'];
var rvals = [0];
function call_next_func() {
if (funcs.length == 0) {
console.log('done');
} else {
var funcname = funcs.shift();
console.log(funcname);
rvals.push(window[funcname](rvals));
call_next_func();
}
}
$(document).ready(function($){
call_next_func();
});
</script>
</body>
</html>
To chain jquery ajax calls i did :
function A(){
return $.ajax({
url: url,
type: type,
data: data,
datatype: datatype,
success: function(data)
{
code here
}
});
}
function B(){
return $.ajax({
url: url,
type: type,
data: data,
datatype: datatype,
success: function(data)
{
code here
}
});
}
function C(){
return $.ajax({
url: url,
type: type,
data: data,
datatype: datatype,
success: function(data)
{
code here
}
});
}
A().done(function(data){
B().done(function(data){
C();
})
});