Reputation: 4259
I have a simple chain of events:
I used to just the chain these functions, each calling the next when it had completed. However, its not very obvious what's going (calling getColumnsFromMeta
results in the view being populated). So in the interest of clarity and code re-use I'd like to refactor these using JQuery
Promises
. I have used promises before. But how do I chain more than two? getColumnsFromMeta ().then(loadSourceFromDatabase /*some arguments*/) //.then(renderList)?;
Here's an example of the getColumnsFromMeta
:
var getColumnsFromMeta = function(id)
{
var sql,
dfd;
dfd = $.Deferred();
var onSuccess = function(tx, result)
{
var columns = [];
for (var i = 0; i < result.rows.length; i++)
{
columns.push(result.rows.item(i).Column);
}
dfd.resolve(columns);
};
var onError = function(tx, error)
{
dfd.reject(error);
};
sql = "SELECT Column FROM Meta WHERE id = ?";
database.query(sql, [id], onSuccess, onError);
return dfd.promise();
};
Upvotes: 13
Views: 12935
Reputation: 21801
zerkms's reply helped me after some thought. I'm going to post what I did here in case an example with full context is helpful.
/**
* takes a list of componentIDs to load, relative to componentRoot
* returns a promise to the map of (ComponentID -> componentCfg)
*/
function asyncLoadComponents (componentRoot, components) {
var componentCfgs = {};
function asyncLoadComponentCfg(component) {
var url = _.sprintf("%s/%s", componentRoot, component);
var promise = util.getJSON(url);
promise.done(function(data) {
componentCfgs[component] = data;
});
return promise;
}
var promises = _.map(components, asyncLoadComponentCfg);
var flattenedPromise = $.when.apply(null, promises);
var componentCfgPromise = flattenedPromise.pipe(function() {
// componentCfgs is loaded now
return $.Deferred().resolve(componentCfgs).promise();
});
return componentCfgPromise;
}
var locale = 'en-US';
var componentRoot = '/api/components';
var components = ['facets', 'header', 'DocumentList'];
$.when(asyncLoadComponents(componentRoot, components)).done(function(componentCfgs) {
buildDocumentListPage(locale, componentCfgs)
});
Upvotes: 4
Reputation: 254896
It should be something like:
function getColumnsFromMeta()
{
var d = $.Deferred();
// retrieve data in async manner and perform
// d.resolve(columns);
return d.promise();
}
function loadSelectedColumns(columns)
{
var d = $.Deferred();
// retrieve data in async manner and perform
// d.resolve(data);
return d.promise();
}
function render(data)
{
// render your data
}
getColumnsFromMeta().pipe(loadSelectedColumns).pipe(render);
http://jsfiddle.net/zerkms/xYDbm/1/ - here is a working sample
http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/ -- this is the article I really like about promises
Upvotes: 27