Overview
You have a couple of choices. You can have your code using those functions look like this, using callbacks:
getStuff(function(results) {
getMoreStuff(results, doSomethingWithStuff);
});
or like this, using jQuery's Deferred
and Promise
objects:
getStuff().then(getMoreStuff).then(doSomethingWithStuff):
Using callbacks
Have both getStuff
and getMoreStuff
accept an argument that is a callback to call when they're done, e.g.:
function getStuff(callback) {
// ^------------------------------ callback argument
$.ajax({
...
success: function(results) {
// other functions involving results
callback(results);
// ^------------------------------------ use the callback arg
}
});
}
...and similarly for getMoreStuff
.
Using Deferred
and Promise
jQuery's ajax
function integrates with its Deferred
and Promise
features. You can just add return
to your existing functions to make that work, e.g.:
function getStuff(callback) {
return $.ajax({
...
});
}
(Note: No need for the success
callback.)
Then this code:
getStuff().then(getMoreStuff).then(doSomethingWithStuff);
does this:
getStuff
starts its ajax
call and returns the Promise
that call creates.
When that ajax
call completes and resolves the promise, getMoreStuff
is called with the results of the ajax
call as its first argument. It starts its ajax
call.
When getMoreStuff
's ajax
call completes, doSomethingWithStuff
is called with the results of that call (the one in getMoreStuff
).
It's important to use then
, not done
, in order to get the correct results passed on at each stage. (If you use done
, both getMoreStuff
and doSomethingWithStuff
will see the results of getStuff
's ajax
call.)
Here's a full example using ajax
:
Fiddle | Alternate Fiddle with the ajax
calls taking one second each (makes it easier to see what's happening)
function getStuff() {
display("getStuff starting ajax")
return $.ajax({
url: "/echo/json/",
type: "POST",
data: {json: '{"message": "data from first request"}'},
dataType: "json"
});
}
function getMoreStuff(results) {
display("getMoreStuff got " + results.message + ", starting ajax");
return $.ajax({
url: "/echo/json/",
type: "POST",
data: {json: '{"message": "data from second request"}'},
dataType: "json"
});
}
function doSomethingWithStuff(results) {
display("doSomethingWithStuff got " + results.message);
}
getStuff().then(getMoreStuff).then(doSomethingWithStuff);
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
Output:
getStuff starting ajax
getMoreStuff got data from first request, starting ajax
doSomethingWithStuff got data from second request
You don't need to be using ajax
to get the benefit of this, you can use your own Deferred
and Promise
objects, which lets you write chains like this:
one().then(two).then(three);
...for any situation where you may have asynchronous completions.
Here's a non-ajax
example:
Fiddle
function one() {
var d = new $.Deferred();
display("one running");
setTimeout(function() {
display("one resolving");
d.resolve("one");
}, 1000);
return d.promise();
}
function two(arg) {
var d = new $.Deferred();
display("Two: Got '" + arg + "'");
setTimeout(function() {
display("two resolving");
d.resolve("two");
}, 500);
return d.promise();
}
function three(arg) {
var d = new $.Deferred();
display("Three: Got '" + arg + "'");
setTimeout(function() {
display("three resolving");
d.resolve("three");
}, 500);
return d.promise();
}
one().then(two).then(three);
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
Output:
one running
one resolving
Two: Got 'one'
two resolving
Three: Got 'two'
three resolving
These two (the ajax
example and the non-ajax
example) can be combined when necessary. For instance, if we take getStuff
from the ajax
example and we decide we have to do some processing on the data before we hand it off to getMoreStuff
, we'd change it like this: Fiddle
function getStuff() {
// Create our own Deferred
var d = new $.Deferred();
display("getStuff starting ajax")
$.ajax({
url: "/echo/json/",
type: "POST",
data: {json: '{"message": "data from first request"}', delay: 1},
dataType: "json",
success: function(data) {
// Modify the data
data.message = "MODIFIED " + data.message;
// Resolve with the modified data
d.resolve(data);
}
});
return d;
}
Note that how we use that didn't change:
getStuff().then(getMoreStuff).then(doSomethingWithStuff);
All that changed was within getStuff
.
This is one of the great things about the whole "promise" concept (which isn't at all specific to jQuery, but jQuery gives us handy versions to use), it's fantastic for decoupling things.