Reputation: 170
Another one that should be simple, but is giving me trouble. I am trying to learn about jQuery's .Deferred() and .promise() functionality to delay certain actions until a recursive function is completely finished. Currently, my code is similar to the following:
function showText(setTarget, setMessage, setIndex, setInterval) {
var defer = jQuery.Deferred();
var doShowText = function (target, message, index, interval) {
if (index < message.length) {
$(target).append(message[index++]);
setTimeout(function () { doShowText(target, message, index, interval); }, interval);
}
else {
alert("Done!");
defer.resolve();
}
};
doShowText(setTarget, setMessage, setIndex, setInterval);
return defer.promise();
}
function startButtonClick() {
displayElement($("#getElement"));
showText($("#getElement > h1"), "This text will slowly write to the screen.", 0, 50).promise()
.then(alert("Finished."));
}
When this runs, "Finished" alert (which I am trying to defer) runs after the first execution of the recursive script, so it will appear when only one letter has been printed (not the intended result). The "Done" alert, however, appears correctly once all of the letters have been printed and the recursion is finished, so it seems as though my defer variable should not be resolved until then. Can anyone help me discover why the "Finished" alert is being called early here? Any assistance is appreciated!
EDIT: I realized I had accidentally posted a slightly older version of my code. It has been updated with the correct version (the behavior at run time is the same).
Upvotes: 1
Views: 2249
Reputation: 364
Here's a fiddle with the whole lot wrapped up in a nice little object: http://jsfiddle.net/YVZKw/3/
As plalx and ctcherry have already stated, the biggest problem was the lack of a function in your .then call.
HTML
<a href='javascript:void(0)'>Start</a>
<div id='sampleElement'>
<h1></h1>
</div>
JavaScript
$('a').on('click', function(){
new ShowText(
$("#sampleElement > h1"),
"I will slowly write text to the screen.",
50
)
.done(function(){
alert("Finished.")
});
});
function ShowText(target, message, speed)
{
me = this;
me.target = target;
me.message = message;
me.index = 0;
me.speed = speed;
me.defer = new $.Deferred();
me.interval = setInterval(function() {
me.target.append(me.message[me.index++]);
if (me.index > me.message.length) {
clearInterval(me.interval);
me.defer.resolve();
}
}, me.speed);
return me.defer;
}
Upvotes: 1
Reputation: 43718
That's happening because you are actually executing the alert
function right away instead of passing a function reference.
Do this instead:
.then(alert.bind(null, 'finished'));
Or
.then(function () {
alert('finished');
});
Upvotes: 2
Reputation: 28554
In the startButtonClick
function you don't need to call .promise()
on the result of showText
, because you are already doing that inside of showText
. Next, the argument to the then
callback should be a function, right now you are immediately calling the alert function, not passing it as a function, which is why it is displaying immediately, so just wrap it in a function:
function(){ alert("Finished."); }
Here is a jsfiddle with the code: http://jsfiddle.net/RnLXF/
Upvotes: 2