javascript竞争条件

时间:2009-01-09 02:01:59

标签: javascript

我正在调用一个javascript函数,它可以快速连续地设置iframe的不透明度。基本上这个从0到100的alpha补间。 这是代码


   function setAlpha(value)
   {
       iframe.style.opacity = value * .01;
       iframe.style.filter = 'alpha(opacity =' + val + ')';
   }

我的问题是,它第一次工作在ie(7)而不是firefox(3.02)。在Firefox中我得到一个延迟,然后contentdocument显示不透明度为100.如果我坚持它的工作,所以我猜它是一个竞争条件(虽然我认为javascript是单线程)和setAlpha函数在最后一个函数执行完毕之前被调用。 任何帮助将不胜感激。我已经阅读了'避免javascript竞争条件',但我认为这符合不同的条件(加上我无法弄清楚如何将这个例子应用到这个例子中)。

4 个答案:

答案 0 :(得分:5)

问题是大多数浏览器在javascript执行暂停之前不会重新绘制。

这可以通过使用setTimeout来解决,正如其他人所建议的那样。但是,我建议使用jQuery或任何javascript库之类的东西来制作动画。运行setTimeout 100次是一个坏主意,因为动画的长度将根据浏览器和用户计算机的速度而变化。执行动画的正确方法是指定它们应该持续多长时间并检查系统时间以确定动画应该进展多远。

function fadeIn(elem,animation_length) {
   var start = (new Date()).getTime();

   var step = function() {
      window.setTimeout(function() {
        var pct = ((new Date()).getTime() - start)/animation_length;
        elem.style.opacity = Math.min(pct,1);
        if (pct < 1) 
           step();
      },20);
   };
   step();
}

[edit:]上面的代码只是为了说明如何根据系统时钟而不是简单的间隔来制作动画。请使用库来制作动画。上面的代码不适用于IE,因为IE使用“filter:opacity(xx)”而不是“opacity”。图书馆会为您处理这个问题,并提供很好的功能,例如完成事件,以及取消动画的功能。

答案 1 :(得分:1)

Javascript不会跨多个线程运行,因此您可以安全地避免竞争条件(忽略Safari和Firefox中即将推出的工作线程支持:D)。

简单的问题,你如何多次调用setAlpha,firefox,safari和opera所有合并样式表更新 - 例如。除非必须,否则他们不会在js运行时重新绘制甚至重新绘制样式信息。所以他们只会在JS完成时画画。

所以,如果你正在做

while(...) setAlpha(...)

它们不会更新,您可能需要使用setTimeout来触发多个不同的调用来更新样式。

另一种方法是使用jQuery,mootools等库,我隐约回想起提供了一种简化的机制来完成这些类型的动画和过渡。作为一个额外的奖励,我相信至少有一些库在可用时也会使用webkit过渡和动画css规则(例如Safari,并且我认为最新的firefox构建版本)

[编辑:警告:我实际上并没有使用这些库中的任何一个,我只阅读了他们应该做的事情。我的网站在lynx中呈现与任何其他浏览器相同的效果,因为我无法从纸袋中设计出来:D]

答案 2 :(得分:0)

有些浏览器足够聪明,可以延迟对DOM的更改,直到调用堆栈为空。

这通常是一件很聪明的事情。例如,如果您调用一个将元素更改为黄色的函数,并立即调用将相同元素更改回原始状态的函数,则浏览器不应该浪费时间进行更改,因为它应该快速发生。让用户察觉不到。

setTimeout(func, 0)技巧通常用于强制Javascript延迟执行func,直到调用堆栈为空。

在代码中:

function setAlpha(opacity){
   some_element.style.opacity = opacity;
}

/** 
* This WON'T work, because the browsers won't bother reflecting the 
* changes to the element's opacity until the call stack is empty, 
* which can't happen until fadeOut() returns (at the earliest)
**/
function fadeOut(){
    for (var i=0; i<10; i++){
        setAlpha(0.1*i);    
    }
}

/** 
* This works, because the call stack will be empty between calls
* to setAlpha()
**/
function fadeOut2(){
    var opacity = 1;
    setTimeout(function setAlphaStep(){
        setAlpha(opacity);
        if (opacity > 0){
            setTimeout(setAlphaStep, 10);        
        }
        opacity -= 0.1;  
    }, 0);
}

所有这些归结为使用众多javascript库中的一个来为你处理这些棘手的东西。

编辑:这是good article on the tricky Javascript call stack

答案 3 :(得分:0)

您使用的是setTimeout还是紧凑循环?如果您只使用循环来调用该函数,则切换到使用setTimout。

示例:

   function setAlpha(value)
   {
       iframe.style.opacity = value * .01;
       iframe.style.filter = 'alpha(opacity =' + val + ')';
       if(value < 100 ) {
       setTimeout(function () {setAlpha(value+1)},20);
       }
   }
   setAlpha(0);

因为你看,它不仅仅是单线程的javascript。这是整个该死的浏览器。如果你的javascript进入了一个紧凑的循环,你挂起整个浏览器。所以浏览器暂停等待javascript完成,甚至没有机会更新屏幕,而你的代码正在迅速改变一些dom值。