浏览器呈现和javascript执行的同步/异步特性

时间:2012-03-23 10:55:53

标签: javascript jquery asynchronous

我有一些处理需要几秒钟,所以我想在它正在进行时添加一个可视指示器。

.processing
{
  background-color: #ff0000;
}

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

脚本:

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

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

我知道JS是单线程的,但我总是假设浏览器渲染会在DOM更新时同步运行。我的假设不正确吗?

更重要的是,实现这种效果的推荐方法是什么?我是否必须使用setTimeout使慢速处理异步并使用回调?必须有更好的方法,因为我真的不需要异步行为;我只想刷新UI。

编辑:

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

(注意,您可能需要调整循环迭代次数,以便在您自己的PC上为您提供合理的延迟)

4 个答案:

答案 0 :(得分: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);

或类似且更优化的东西。 你的代码会更复杂,速度更慢......但是你要阻止浏览器冻结,你可以制作一个高级的预加载状态栏。

答案 1 :(得分:1)

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

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

这样浏览器应该使用processing重绘屏幕,而长时间运行的步骤将作为单独的事件启动,并在完成后删除处理消息。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

浏览器在重绘和何时重排时非常自由。不同的引擎会显示不同的行为。如需进一步阅读,请参阅When does reflow happen in a DOM environment?。在您的示例中,浏览器会看到您添加一个类并在此之后直接删除它,因此他可能不会执行任何可见的操作。

为避免这种情况,您可以应用强制重绘,或者使计算与WebWorkers或setTimeout等异步。