如何防止javascript函数的并发执行?

时间:2010-03-23 06:16:50

标签: javascript jquery

我正在使用jQuery制作类似于The Huffington Post的“From the AP”的自动收报机。股票代码通过用户命令(单击箭头)或自动滚动来旋转ul。

默认情况下,每个列表项都显示为:none。通过添加“showHeadline”类来显示它是display:list-item。 UL的HTML看起来像这样:

        <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>

当用户单击右箭头或自动滚动setTimeout关闭时,它会运行tickForward()函数:

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()
  }
     };

Jon Raasch's A Simple jQuery Slideshow.的启发 基本上,找到可见的,接下来应该看到的内容,使可见的东西淡化并删除将其标记为可见的类,然后淡入下一个并添加使其可见的类。

现在,如果自动滚动正在运行,那么一切都很糟糕,每三秒就开始一次tickForward()。但是,如果用户反复单击箭头按钮,则会产生两个负面条件:

  1. 而不是仅仅通过列表快速推进点击次数,它会无限期地继续以比正常速度更快的速度滚动。
  2. 它可以产生两个(或更多)列表项被赋予.showHeadline类的情况,因此列表上有重叠。
  3. 我可以看到这些情况发生(特别是#2),因为tickForward()函数可以与自身同时运行,产生不同的$ active和$ next。

    所以我认为我的问题是: 防止并发执行tickForward()方法的最佳方法是什么?

    我尝试或考虑过的一些事情:

    • 设置标志:当tickForward()运行时,它会将isRunning标志设置为true,并在结束前将其设置为false。如果isRunning为false,则事件处理程序的逻辑设置为仅调用tickForward()。我尝试了一个简单的实现,isRunning似乎永远不会改变。

    • jQuery队列():我认为排队tickForward()命令会很有用,所以如果你快速点击它五次,它仍会按照命令运行,但不会同时运行。但是,在我粗略阅读这个主题时,似乎必须将队列附加到其队列所适用的对象上,并且由于我的tickForward()方法影响多个lis,我不知道我在哪里附加它。

3 个答案:

答案 0 :(得分:1)

您可以设置bool变量并在函数执行时检查它的值。如果它已经运行你可以等待它结束或者只是跳过它(不知道哪个是可取的)

var active = false;
function tickForward() {
    if (!active) {
        active = true;

        // your function code goes here

        // remember to change active to false once you're done
        active = false;
    } else {
        // if you want to skip if active = true you don't need this else
        // if you want to call your method later use something like this
        setTimeout(tickForward, 1000);
    }
}

答案 1 :(得分:1)

你不能在javascript中拥有函数的并发执行。你只需要几个调用等待执行的顺序执行。因此,在函数运行时设置标志不起作用。当事件处理程序运行时,它不能与tickHandler()执行同时运行(javascript是无线程的)。

现在你必须准确定义你想要的东西,因为它没有出现在你的问题中。当用户快速连续点击箭头时,你会发生什么?点击如何干扰自动滚动?

我认为最简单的方法是仅在代码空闲时处理点击,因此连续3次点击只会勾选一次。并且您点击替换自动滚动并重置其计时器。所以我使用了一个标记ticksInQueue,当一个标记排在一边排队时,它会被抬起,只有当fadeIn完成时才降低

    var ticksInQueue = 0,
        timerId = setInterval(tickForward, 5000),
        isPaused = false;

    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');
                    if(ticksInQueue) ticksInQueue--; // only change
                })}, 1000);

        if(isPaused == true){
        } else {
            startScroll()
        }
   }

    $('#arrow').click(function () {
        if(ticksInQueue) return;
        ticksInQueue++;
        clearInterval(timerId);
        timerId = setInterval(tickForward, 5000);
        tickForward();
    });

您可以在此处尝试演示:http://jsfiddle.net/mhaCF/

答案 2 :(得分:0)

此实现使用“new Date()”和“setTimeout”来避免某些资源的某些竞争条件(并发执行)。这样每次调用“NoConcurrExecFunc”的时间至少相差750毫秒!

{{1}}