2012-03-23 94 views
6

我有一些处理过程需要几秒钟的时间,所以我想在处理过程中添加一个可视化指示器。浏览器渲染和javascript执行的同步/异步性质

.processing 
{ 
    background-color: #ff0000; 
} 

<div id="mydiv"> 
    Processing 
</div> 

脚本:

$("#mydiv").addClass("processing"); 
// Do some long running processing 
$("#mydiv").removeClass("processing"); 

我天真地以为类将应用于div和用户界面将被更新。但是,在浏览器中运行(至少在Firefox中),div永远不会突出显示。有人可以向我解释为什么我的div不会突出显示吗?该类被添加,处理发生,然后该类被删除;用户界面不会在两者之间更新,用户也不会看到红色背景。

我知道JS是单线程的,但我总是推测浏览器呈现将在DOM更新时同步运行。我的假设是否不正确?

更重要的是,达到这种效果的推荐方法是什么?我是否必须使用setTimeout来使缓慢处理异步并使用回调工作?必须有更好的方法,因为我在这里确实不需要异步行为;我只想要刷新UI。

编辑:

的jsfiddle:http://jsfiddle.net/SE8wD/5/

(请注意,你可能需要调整循环迭代的次数,给你自己的电脑上合理的延迟)

+1

我相信这样的改变会直接导致DOM,但浏览器会等待,直到JavaScript执行“空闲”,然后再进行重新绘制。 – pimvdb 2012-03-23 10:58:48

+0

“做一些长时间运行的处理”有一些异步调用? (ajax,settimeout等) – 2012-03-23 11:00:31

+0

我不能真正看到你的代码出错了,因为它看起来很好,你已经发布。也许在你的外部css文件中有一些'!important'。你确定增加了班级(你是否通过萤火虫检查过),这个过程真的需要很长时间吗?可能它发生得太快 – 2012-03-23 11:00:44

回答

0

Force a redraw。或者,使用Soren's code,因为UI线程上的处理听起来很糟糕...

+0

但是,jQuery插件似乎目前处于关闭状态。增加了一个替代品这可能是因为你不需要推迟'n.parentNode.removeChild(n)';如果是这样,请随时编辑我的答案。 – 2012-03-23 11:57:09

+0

我在我的jsFiddle上尝试过这个,但没有成功。我显然没有正确使用它。你可以加小提琴吗? – njr101 2012-03-23 13:00:54

+0

对不起,但小提琴不起作用。该类将被应用,然后脚本会以脚本错误中止。 “完成”消息从不显示。 – njr101 2012-03-23 14:17:15

1

我不建议冻结重量脚本的浏览器。 在这种情况下,我更喜欢更改不冻结浏览器和DOM的代码。 如果在你的代码中有一些循环,你可以使用延迟调用一个函数(使用setInterval)并在某处存储一些状态变量。 例如:

for(var i=0; i<1000000; i++) { 
    // do something 
} 

可以是:

var i = 0; 
var intervalID; 
var _f = function() { 
    // do something 
    i++; 
    if(i==1000000) clearInterval(intervalID); 
} 
intervalID = setInterval(_f,1); 

或类似和更优化的东西。 你的代码会更复杂一点,而且速度更慢。但是这样可以防止浏览器冻结,并且可以创建高级预加载状态栏。

+0

谢谢,这就是我提到'setTimeout'时的意思。我意识到我可以做到这一点,但我希望避免增加异步调用的复杂性。当异步回调发生时,状态不确定会打开一整类其他问题。 – njr101 2012-03-23 12:50:04

+0

我知道。你也可以看到网络工作者http://www.html5rocks.com/en/tutorials/workers/basics/ – 2012-03-23 12:52:57

+0

感谢您的链接。有趣的东西,但不幸的是不会帮助我,因为我需要跨浏览器支持。 – njr101 2012-03-23 13:17:36

1

你可能应该把处理放在一个单独的事件中,像这样;

$("#mydiv").addClass("processing"); 
setTimeout(function(){ 
    // This runs as a seperate event after 
    // Do some long running processing 
    $("#mydiv").removeClass("processing"); 
},1); 

这样的浏览器应重绘屏幕与processing,而长跑步步揭开序幕,作为一个单独的事件,并在完成后删除处理消息。

+0

谢谢,这是我怀疑。我真的希望有一种方法可以避免浏览器在更改DOM后重新呈现此强制。 – njr101 2012-03-23 14:20:36

+0

警告 - 我发现它在5ms超时后开始工作 - 任何更短和铬仍然等待。 – user2486570 2016-12-11 21:22:56

+0

@ user2486570 - 我怀疑它 - JS是单线程事件循环 - 所以它只有你自己创建的竞争条件 - 如果你看到一个5ms的问题可能是因为你有别的东西运行一个计时器或一个与您的其他代码冲突的DOM就绪事件。 – Soren 2016-12-11 21:28:59

0

浏览器对于何时重新绘制以及何时回流是非常自由的。不同的引擎会显示不同的行为。有关进一步阅读,请参阅When does reflow happen in a DOM environment?。在你的例子中,浏览器会看到你添加了一个类,然后直接删除它,所以他可能不会做任何可见的事情。为了避免这种情况,你可以应用一个强制重画黑客攻击,或者使你的计算与WebWorkers或setTimeout等进行异步处理。