Reputation: 16719
My experience with $.Deferred
is very limited, and so far the code the came looks very messy.
The function that is supposed to return a promise is updating the DOM with HTML received after an ajax request.
It's used like this:
this._refreshWorkspace(response.html).then(function(){
// do things after DOM update finished
});
Here's the function code:
_refreshWorkspace: function(htmlBlocks){
var dfd = $.Deferred();
if('editor' in htmlBlocks){
app.destroy(this.editor).then((function(){
this.editor.empty().append(htmlBlocks.editor);
}).bind(this)).then((function(){
app.refresh(this.editor);
}).bind(this));
}
if('listPanels' in htmlBlocks){
app.destroy(this.list).then((function(){
this.list.empty().append(htmlBlocks.listPanels);
}).bind(this)).then((function(){
app.refresh(this.list);
// other unrelated code here
dfd.resolve();
}).bind(this));
}
if('listNav' in htmlBlocks){
// similar code block
}
return dfd;
},
It seems to work but only if the "listPanels" htmlBlock is provided.
I want the dfd
to be resolved once after all refresh calls, or even better if possible - after all refresh calls are resolved. Any ideas on how could I make this happen?
Upvotes: 1
Views: 714
Reputation: 19288
As already explained, the simple answer to the question is to aggregate the individual promises with $.when.apply(null, promiseArray)
.
However, if all the code in the question is representative of the treatment to be applied to all html blocks, then you can go further.
jQuery.map()
will operate on ownProperties of an object so can be exploited to iterate over htmlBlocks
, resulting in a concise, generalised main routine with a couple of supporting hashes.
_refreshWorkspace: function(htmlBlocks) {
var that = this; // avoids the need for .bind(this) in the promise chain and the methodsHash
var propHash = {
'editor': 'editor',
'listPanels': 'list'
};
// All the "other unrelated code" is defined here
var methodsHash = {
'editor': null,
'listPanels': function(key, obj) {
...
},
...
};
//And the main routine is a concise $.map(htmlBlocks, ...) structure.
var promises = $.map(htmlBlocks, function(html, key) {
var obj = that[propHash[key]];
return app.destroy(obj).then(function() {
obj.empty().append(html); //if empty() and append() are jQuery methods then this line is synchronous.
return app.refresh(obj);// if app.destroy() is asynch and theanable, then it seems safe to assume that app.refresh() is also asynch and theanable. Therefore return the result here.
}).then(function() {
if(methodsHash[key]) {
methodsHash[key](key, obj);
}
});
});
//Now aggregate `promises` into a single promise which resolves when all the promises resolve, or rejects when any of the promises rejects.
return $.when.apply(null, promises);
},
Now, to cater for all other html blocks, just add one line to propHash
and a null or function to methodsHash
. Providing the main routine is comprehensive, it won't need amending.
IMHO, this a better way to organise the code.
Upvotes: 1
Reputation: 1074355
Put all of the promises from the loops into an array, then use $.when
. Sadly, using $.when
with an array is ugly:
return $.when.apply($, theArray);
...because $.when
is designed to accept discrete arguments rather than an array.
Something like this:
_refreshWorkspace: function(htmlBlocks){
var promises = [];
if('editor' in htmlBlocks){
promises.push(
app.destroy(this.editor).then((function(){
this.editor.empty().append(htmlBlocks.editor);
}).bind(this)).then((function(){
app.refresh(this.editor);
}).bind(this))
);
}
if('listPanels' in htmlBlocks){
promises.push(
app.destroy(this.list).then((function(){
this.list.empty().append(htmlBlocks.listPanels);
}).bind(this)).then((function(){
app.refresh(this.list);
}).bind(this))
);
}
if('listNav' in htmlBlocks){
// similar code block
}
return $.when.apply($, promises);
},
Here's a live example using random Deferred
s:
function doSomething() {
var promises = [];
var d1, d2, d3;
d1 = new $.Deferred();
promises.push(d1.promise());
setTimeout(function() {
snippet.log("Resolving d1");
d1.resolve(1);
}, Math.floor(Math.random() * 1000));
d2 = new $.Deferred();
promises.push(d2.promise());
setTimeout(function() {
snippet.log("Resolving d2");
d2.resolve(2);
}, Math.floor(Math.random() * 1000));
d3 = new $.Deferred();
promises.push(d3.promise());
setTimeout(function() {
snippet.log("Resolving d3");
d3.resolve(3);
}, Math.floor(Math.random() * 1000));
return $.when.apply($, promises);
}
// Use it
doSomething().then(function() {
snippet.log("All resolved");
});
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Upvotes: 1