English 中文(简体)
JavaScript竞争条件
原标题:
  • 时间:2009-01-09 02:01:58
  •  标签:

I m calling a javascript function that sets the opacity of an iframe an unknown amount of times in rapid succession. Basically this tweens the alpha from 0 to 100. here is the code


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

My problem is that for the first time it is working in ie (7) and not in firefox (3.02). in Firefox I get a delay and then the contentdocument appears with an opacity of 100. If I stick an alert in it works, so I m guessing it is a race condition (although I thought javascript was single threaded) and that the setAlpha function is being called before the last function has finished executing. Any help would be greatly appreciated. I ve read the avoiding a javascript race condition post but I think this qualifies as something different (plus I can t figure out how to apply that example to this one).

问题回答

问题在于大多数浏览器直到javascript执行暂停后才重新绘制。

这可以通过使用setTimeout来解决,正如其他人所建议的那样。然而,我建议使用类似jQuery或任何JavaScript库来进行动画。运行100次setTimeout是个坏主意,因为动画的长度会根据浏览器和用户电脑的速度而变化。正确的做法是指定动画持续的时间,并检查系统时间来确定动画的进度。

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

以上代码仅用于说明如何基于系统时钟执行动画,而非简单间隔。请使用库来执行动画。由于IE使用“filter:opacity(xx)”而非“opacity”,因此以上代码在IE上无法工作。库将为您处理此问题,并提供完美的功能,如完成事件和取消动画的能力。

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

简单问题,你是如何多次调用setAlpha的,Firefox、Safari和Opera都会合并样式表更新——例如,他们不会在JavaScript运行时重新绘制或重新计算样式信息,除非必须如此。因此,它们只会在JS完成后绘制。

如果您正在进行这个操作

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

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

另一种选择是使用类似jQuery、mootools等的库,我模糊地记得它们提供了简化的机制来执行这些类型的动画和转换。作为附加奖励,我相信至少一些库还会在可用时使用WebKit转换和动画CSS规则(例如Safari和最新的Firefox版本)。

[注意:我实际上没有使用过这些库,我只是阅读了关于它们应该做什么的资料。我的网站在Lynx和其他浏览器中呈现相同的效果,因为我设计不好。:D]

你是在使用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值。

有些浏览器足够聪明,可以延迟对文档对象模型(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库之一的绝妙借口。

编辑:这里有一篇有关于 Javascript 调用栈的棘手问题的好文章





相关问题
热门标签