I just want to do three things in order.
My table has thousands of rows and takes a while to sort. I would like my "waiting" modal to display during the sort, but the page waits for the sorting to complete before attempting to display the modal.
Here's a jsfiddle. (You can increase or decrease bignumber
to get a sorting delay you can see but isn't annoying.)
I first tried:
//CSS: body.loading .waitingModal {display: block;}
$('body').toggleClass("loading"); // happens second!
sortTable(currentdirection,columnno); // takes ages and happens first
$('body').toggleClass("loading"); // happens third
If I comment out the last line or cause an error before it, the table sorts, then the modal appears.
Thanks to this question, my next attempt worked once but then the loading modal never came again:
$('body').toggleClass("loading");
setTimeout(function() { //loading works but not if you click again
sortTable(order,1);
$('body').toggleClass("loading");
}
Web pages advising people suggest I should use jQuery.Deferred
rather than entangle myself in callback hell, and since I couldn't see where to put a callback and callback hell doesn't sound fun I went along with it.
I read questions on How to use jQuery deferred and then vs done and most promisingly, this answer's Example 1 of chaining, but everyone seems to assume I have a deferred/promise already turning up from some ajax call or something.
My first attempt to chain some .then(...)
calls didn't work, because literally nothing happened:
$.Deferred().then(function(){
$('body').toggleClass("loading"); //doesn't happen
}).then(function(){
sortTable(order,1); //doesn't happen
}).then(function(){
$('body').toggleClass("loading"); //doesn't happen
});
Using the beforeStart
parameter got my modal displayed, but nothing else:
$.Deferred(function(it){
$('body').toggleClass("loading"); //happens first, hooray!
}).then(function(){
sortTable(order,1); //doesn't happen
}).then(function(){
$('body').toggleClass("loading"); //doesn't happen
});
I then tried to chain some deferred then
s, resolving and passing the deferred explicitly, but I clearly don't understand how this works:
$.Deferred().then(function(it){
$('body').toggleClass("loading"); //still happens second
it.resolve();
return it.promise();
}).then(function(){
sortTable(order,1); //happens first
}).then(function(){
$('body').toggleClass("loading"); //happens third
});
Lastly, I've tried resolving a deffered
on the change event on the body
function loadingNow(){
var deferred = $.Deferred();
$('body').toggleClass("loading") // happens
.change(function(){
deferred.resolve()
});
return deferred.promise();
}
loadingNow().then(function(){
sortTable(order,1); //doesn't happen
}).then(function(){
$('body').toggleClass("loading"); //doesn't happen
});
Doesn't matter if I toggle the class after adding the change event handler:
function loadingNow(){
var deferred = $.Deferred();
$('body').change(function(){
deferred.resolve()
}).toggleClass("loading"); // happens
return deferred.promise();
}
loadingNow().then(function(){
sortTable(order,1); //doesn't happen
}).then(function(){
$('body').toggleClass("loading"); //doesn't happen
});
Honestly, I didn't expect it to be this difficult to persuade jQuery to do three things in order. Perhaps there's a much simpler thing I haven't thought of. I don't mind if it's callbacks or Deferred
done right, but if it's brief and relatively clear that would be a huge bonus.