I am trying to force a serialized logic onto a set of asynchronous activities on a webpage. I am fairly certain I want to use the jQuery deferred object, but I run into the problem that the functions I want to execute are dependent on when the user decides to make selections by clicking various buttons. I am looking for help doing this using the following jsFiddle idea:
Consider a sequence of 4 buttons. When clicked, each button disables itself and enables the next button. Each button shouldn’t have its event set until after it has been enabled. There’s an added task (in this case an alert) to be done ONLY AFTER the 3rd button has been enabled.
Basic HTML Code
<button id="btn1">Click 1st</button>
<button id="btn2" disabled="disabled">Click 2nd</button>
<button id="btn3" disabled="disabled">Click 3rd</button>
<button id="btn4" disabled="disabled">Click 4th</button>
Tasks In Each Step
var fnDoStageOne = function() {
$("#btn1").one("click", function(event, ui) {
$("#btn1").prop("disabled", true);
$("#btn2").prop("disabled", false);
//STAGE ONE IS ONLY DONE AFTER THIS POINT
});
};
var fnDoStageTwo = function() {
$("#btn2").one("click", function(event, ui) {
$("#btn2").prop("disabled", true);
$("#btn3").prop("disabled", false);
//STAGE TWO IS ONLY DONE AFTER THIS POINT
});
};
var fnDoStageThree = function() {
$("#btn3").one("click", function(event, ui) {
$("#btn3").prop("disabled", true);
$("#btn4").prop("disabled", false);
//STAGE THREE IS ONLY DONE AFTER THIS POINT
});
alert("Shouldn't see this if button 3 isn't active yet");
};
var fnDoStageFour = function() {
$("#btn4").one("click", function(event, ui) {
$("#btn4").prop("disabled", true);
alert("Task complete");
//STAGE FOUR IS ONLY DONE AFTER THIS POINT
});
};
Incorrect Control Logic
var oDeferredObj = $.Deferred();
oDeferredObj.then(fnDoStageOne);
oDeferredObj.then(fnDoStageTwo);
oDeferredObj.then(fnDoStageThree);
oDeferredObj.then(fnDoStageFour);
oDeferredObj.resolve();
The jsfiddle can be seen here: http://jsfiddle.net/sva79/
My initial understanding was that I could just chain the functions into the deferred with the .then() function. Obviously, this doesn’t work as the additional task in step 3 triggers on page load. How would I need to adjust the control or logic of this scenario to put of resolving each step until the appropriate button press has been registered?
Your code sample doesn’t do what you think it does. All you are doing is adding to the list of things to be done directly after
oDeferredObjis resolved. Furthermore, there’s the issue of resolving when a task is actually “done”, which your code doesn’t quite label.This seems like something I’ve addressed before in another question some time ago, but I’m not sure I would like the answer I gave back then, so let me try anew.
What you are seeking is a way to chain new promises together. You also want, I presume, a way to say when promises are resolved (or rejected).
A good way to take data asynchronously from one promise to another is to chain them with
pipe, but as you want to trigger the completion of a task from within a UI event, I’m having trouble imagining something better than what I have below.I won’t swear that this is the best way to do it, the simplest way I can think of to handle this is to create a utility function that takes a given promise and a “task”, creates a new promise, lets your task decide what to do with the promise, but only after the given promise is resolved, and return the new promise.
This then can be used to daisy chain your tasks together:
And obviously, you’ll want to do something to signal that a particular task is done at some point, for example:
Mind you, I’ve only given you a start for when promises succeed. It’d be wise do do a bit more for when they fail. Also, if you want to pass data from one promise to another, you may desire need to
pipe, etc.Full source (modified from yours) in a jsFiddle here