我的问题更复杂,但我已经找到了这个简单的&简明示例:
HTML:
First: <span id="first"></span> <br/>
Then: <span id="second"></span> <br/>
And then: <span id="third"></span> <br/>
And then: <span id="fourth"></span> <br/>
And then: <span id="fifth"></span>
JS(+jQuery(如果重要)):
$(document).ready(function() {
doStuff();
});
function doStuff() {
$("#first").text(new Date().getSeconds());
var i = 1;
while (i < 1000000000) {
i++;
}
$("#second").text(new Date().getSeconds());
var i = 1;
while (i < 1000000000) {
i++;
}
$("#third").text(new Date().getSeconds());
var i = 1;
while (i < 1000000000) {
i++;
}
$("#fourth").text(new Date().getSeconds());
var i = 1;
while (i < 1000000000) {
i++;
}
$("#fifth").text(new Date().getSeconds());
}
还有小提琴。
从这个例子中,我的直觉告诉我$("#first")
应该在$("#fifth")
之前接收它的文本值。然而,它们同时出现。不,这不是JS运行非常快的问题,正如您在示例中看到的,$("#first")
的值比$("#fifth")
低约2秒。
为什么会发生这种情况(以及什么!),存在什么解决方案?
javascript会阻止页面渲染,因此在所有javascript块完成之前,它不会渲染html输出。
JavaScript会在UI完成之前阻止它。它应该是一种单线程语言,然而(正如本文所解释的)它并没有那么简单。
解决您问题的方法是使用setTimeout
。请参阅更新后的简化fiddle(该示例不适合复制粘贴,但我相信它足以展示这个想法):
$(document).ready(function() {
doStuff();
});
function doStuff() {
$("#first").text(new Date().getSeconds());
var i = 1;
setTimeout(function() {
while (i < 1000000000) {
i++;
}
$("#second").text(new Date().getSeconds());
}, 0);
}
setTimeout
确保doStuff
函数完成,然后浏览器处理DOM更改,然后执行另一个函数(传递给setTimeout
方法)。
关于事情为什么会以这种方式发生,可以在这里找到一个简洁明了的解释(感谢@bhspencer提供链接)。简单明了:当JS处理doStuff
方法时,UI被阻塞(由于其单线程性质)。第二个参数设置为0毫秒的对setTimeout
的调用(立即)将另一段代码(setTimeout
的第一个参数)放入处理队列。然后doStuff
方法结束。然后JS处理队列中的下一个项目,这显然是UI更新。同样,当UI更新完成时,队列中的下一项是setTimout
调用添加到那里的内容。