I'm currently building a simple lightbox for my website using jquery.deferred patterns and while doing so I'm trying to maintain a flat chain of asynchronous operations and avoid the infamous nested callback pyramid of doom. Here is what I have so far:
$('#gallery a').click( function() {
var id = $(this).attr('id'); // Id of the current selection
var current = 0; // Array index of the current item
getTpl() // Get and load lightbox template
.then(getData) // Get JSON data of the selected item
.then(getItem) // Pass JSON data and get the item
.then(loadItem) // Use JSON data to create the HTML output
.done(function() {
// Wrap everything up, intialize the lightbox controls
// (requires the JSON data) and load the lightbox
});
});
The first two steps (getTpl, getData) are simple AJAX promises and work fine. I can also pass the JSON data further down the chain to the third step (getItem). That's where the problems start:
Thanks in advance!
If you do it like this:
var id = $(this).attr('id'); // Id of the current selection
var current = 0; // Array index of the current item
getTpl() // Get and load lightbox template
.then(getData) // Get JSON data of the selected item
.then(getItem) // Pass JSON data and get the item
.then(loadItem)
Then, you are leaving it to the promise system to set up the arguments for your next function in the chain. Since the promise system is going to use the return from the previous operation in the chain, if you want to pass arguments through to all of these, then all functions have to participate in passing them on. With this exact structure, there is no way around it.
For more general approaches to sharing data with the chain, see:
How to chain and share prior results with Promises
A common scheme for passing an arbitrary number of variables through like this is to put them on an object and just have each function accept and object and return that object.
var options = {
id: $(this).attr('id'), // Id of the current selection
current: 0 // Array index of the current item
};
getTpl(options) // Get and load lightbox template
.then(getData) // Get JSON data of the selected item
.then(getItem) // Pass JSON data and get the item
.then(loadItem)
Then, each function getTpl()
, getData()
, getItem()
and loadItem()
can expect their first argument to be this option object and they should all resolve with that options object. They are each free to add new properties onto the object that will be passed on through the chain.
You do have some other options for how to structure it. If your functions are inline, then they can directly access part variables within scope:
var id = $(this).attr('id'); // Id of the current selection
var current = 0; // Array index of the current item
getTpl() // Get and load lightbox template
.then(function() {
// code here can directly access current and id variables
}).then(...)
Or, you can call your functions directly and pass them whatever variables they need:
var id = $(this).attr('id'); // Id of the current selection
var current = 0; // Array index of the current item
getTpl() // Get and load lightbox template
.then(function() {
return getData(id, current);
}).then(function(data) {
return getItem(data, id);
}).then(function(item) {
return loadItem(item, current);
});