How do I prevent the concurrent execution of a javascript function?
- by RyanV
I am making a ticker similar to the "From the AP" one at The Huffington Post, using jQuery. The ticker rotates through a ul, either by user command (clicking an arrow) or by an auto-scroll.
Each list-item is display:none by default. It is revealed by the addition of a "showHeadline" class which is display:list-item. HTML for the UL Looks like this:
<ul class="news" id="news">
<li class="tickerTitle showHeadline">Test Entry</li>
<li class="tickerTitle">Test Entry2</li>
<li class="tickerTitle">Test Entry3</li>
</ul>
When the user clicks the right arrow, or the auto-scroll setTimeout goes off, it runs a tickForward() function:
function tickForward(){
var $active = $('#news li.showHeadline');
var $next = $active.next();
if($next.length==0) $next = $('#news li:first');
$active.stop(true, true);
$active.fadeOut('slow', function() {$active.removeClass('showHeadline');});
setTimeout(function(){$next.fadeIn('slow', function(){$next.addClass('showHeadline');})}, 1000);
if(isPaused == true){
}
else{
startScroll()
}
};
This is heavily inspired by Jon Raasch's A Simple jQuery Slideshow.
Basically, find what's visible, what should be visible next, make the visible thing fade and remove the class that marks it as visible, then fade in the next thing and add the class that makes it visible.
Now, everything is hunky-dory if the auto-scroll is running, kicking off tickForward() once every three seconds. But if the user clicks the arrow button repeatedly, it creates two negative conditions:
Rather than advance quickly through the list for just the number of clicks made, it continues scrolling at a faster-than-normal rate indefinitely.
It can produce a situation where two (or more) list items are given the .showHeadline class, so there's overlap on the list.
I can see these happening (especially #2) because the tickForward() function can run concurrently with itself, producing different sets of $active and $next.
So I think my question is:
What would be the best way to prevent concurrent execution of the tickForward() method?
Some things I have tried or considered:
Setting a Flag: When tickForward() runs, it sets an isRunning flag to true, and sets it back to false right before it ends. The logic for the event handler is set to only call tickForward() if isRunning is false. I tried a simple implementation of this, and isRunning never appeared to be changed.
The jQuery queue(): I think it would be useful to queue up the tickForward() commands, so if you clicked it five times quickly, it would still run as commanded but wouldn't run concurrently. However, in my cursory reading on the subject, it appears that a queue has to be attached to the object its queue applies to, and since my tickForward() method affects multiple lis, I don't know where I'd attach it.