前言:
其他一些答案是正确的,但实际上并未说明要解决的问题是什么,因此我创建了此答案以提供详细说明。
因此,我将详细介绍浏览器的功能以及如何使用setTimeout()
helps。它看起来很长,但实际上非常简单明了-我只是非常详细地介绍了它。
更新:我做了一个JSFiddle来现场演示下面的说明:http : //jsfiddle.net/C2YBE/31/。非常感谢 @ThangChung帮助启动了它。
UPDATE2:以防万一JSFiddle网站死亡或删除代码,我在最后将代码添加到了此答案中。
细节:
想象一个带有“执行某项操作”按钮和一个结果div的Web应用程序。
onClick
“执行某事”按钮的处理程序调用函数“ LongCalc()”,该函数执行以下两项操作:
进行很长的计算(例如需要3分钟)
将计算结果打印到结果div中。
现在,您的用户开始对此进行测试,单击“执行某事”按钮,页面坐在那里似乎在3分钟内什么都没做,他们变得不安,再次单击该按钮,等待1分钟,什么也没有发生,再次单击按钮...
问题很明显-您需要一个“状态” DIV,以显示正在发生的事情。让我们看看它是如何工作的。
因此,您添加了一个“状态” DIV(最初为空),然后修改onclick
处理程序(函数LongCalc()
)以执行以下4件事:
在状态DIV中填充状态“正在计算...可能需要3分钟”
进行很长的计算(例如需要3分钟)
将计算结果打印到结果div中。
将状态“计算完成”填充到状态DIV中
而且,您很乐意将该应用程序提供给用户进行重新测试。
他们回来时看起来很生气。并说明一下,当他们单击按钮时,状态DIV从未更新为“正在计算...”状态!
您挠头,在StackOverflow上询问(或阅读文档或Google),并意识到问题所在:
浏览器将事件产生的所有“ TODO”任务(UI任务和JavaScript命令)都放在一个队列中。不幸的是,用新的“正在计算...”值重新绘制“状态” DIV是一个单独的TODO,它将到达队列的结尾!
这是用户测试期间事件的细分,每个事件之后的队列内容:
- 队列:
[Empty]
- 事件:单击按钮。事件发生后排队:
[Execute OnClick handler(lines 1-4)]
- 事件:在OnClick处理程序中执行第一行(例如,更改Status DIV值)。事件后排队:
[Execute OnClick handler(lines 2-4), re-draw Status DIV with new "Calculating" value]
。请注意,虽然DOM更改是瞬时发生的,但要重新绘制相应的DOM元素,您需要一个由DOM更改触发的新事件,该事件发生在队列末尾。
- 问题!!! 问题!!!详细说明如下。
- 事件:在处理程序中执行第二行(计算)。在之后排队:
[Execute OnClick handler(lines 3-4), re-draw Status DIV with "Calculating" value]
。
- 事件:在处理程序中执行第三行(填充结果DIV)。在之后排队:
[Execute OnClick handler(line 4), re-draw Status DIV with "Calculating" value, re-draw result DIV with result]
。
- 事件:在处理程序中执行第4行(用“ DONE”填充状态DIV)。队列:
[Execute OnClick handler, re-draw Status DIV with "Calculating" value, re-draw result DIV with result; re-draw Status DIV with "DONE" value]
。
- 事件:
return
从onclick
处理程序子执行隐式执行。我们将“ Execute OnClick处理程序”从队列中移开,然后开始执行队列中的下一项。
- 注意:由于我们已经完成了计算,因此用户已经过去了3分钟。重画事件尚未发生!!!
- 事件:使用“计算”值重新绘制状态DIV。我们进行重画并将其移出队列。
- 事件:使用结果值重新绘制结果DIV。我们进行重画并将其移出队列。
- 事件:使用“完成”值重新绘制状态DIV。我们进行重画并将其移出队列。眼神敏锐的观众甚至可能会注意到“状态DIV带有“计算”值的闪烁几分之一秒- 计算完成后
因此,潜在的问题是,“状态” DIV的重绘事件被放置在队列的末尾,而“执行第2行”事件则需要3分钟,因此实际的重绘不会发生计算完成后。
救援来了setTimeout()
。它有什么帮助?因为通过通过调用了长时间执行的代码setTimeout
,您实际上创建了2个事件:setTimeout
执行本身和(由于0超时)分别为要执行的代码创建队列条目。
因此,要解决您的问题,请将您的onClick
处理程序修改为两个语句(在新函数中或中的一个块onClick
):
在状态DIV中填充状态“正在计算...可能需要3分钟”
执行setTimeout()
0超时并调用LongCalc()
function。
LongCalc()
功能与上次几乎相同,但显然第一步没有“正在计算...”状态DIV更新;而是立即开始计算。
那么,事件序列和队列现在看起来像什么?
- 队列:
[Empty]
- 事件:单击按钮。事件发生后排队:
[Execute OnClick handler(status update, setTimeout() call)]
- 事件:在OnClick处理程序中执行第一行(例如,更改Status DIV值)。事件后排队:
[Execute OnClick handler(which is a setTimeout call), re-draw Status DIV with new "Calculating" value]
。
- 事件:在处理程序中执行第二行(setTimeout调用)。在之后排队:
[re-draw Status DIV with "Calculating" value]
。该队列在0秒内没有任何新内容。
- 事件:0秒后,超时警报将关闭。在之后排队:
[re-draw Status DIV with "Calculating" value, execute LongCalc (lines 1-3)]
。
- 事件:使用“计算”值重新绘制状态DIV。在之后排队:
[execute LongCalc (lines 1-3)]
。请注意,此重画事件实际上可能在警报响起之前发生,效果也一样。
- ...
万岁!在开始计算之前,状态DIV已更新为“正在计算...”!
下面是来自JSFiddle的示例代码,这些示例说明了这些示例:http : //jsfiddle.net/C2YBE/31/:
HTML代码:
<table border=1>
<tr><td><button id='do'>Do long calc - bad status!</button></td>
<td><div id='status'>Not Calculating yet.</div></td>
</tr>
<tr><td><button id='do_ok'>Do long calc - good status!</button></td>
<td><div id='status_ok'>Not Calculating yet.</div></td>
</tr>
</table>
JavaScript代码:(在上执行onDomReady
,可能需要jQuery 1.9)
function long_running(status_div) {
var result = 0;
// Use 1000/700/300 limits in Chrome,
// 300/100/100 in IE8,
// 1000/500/200 in FireFox
// I have no idea why identical runtimes fail on diff browsers.
for (var i = 0; i < 1000; i++) {
for (var j = 0; j < 700; j++) {
for (var k = 0; k < 300; k++) {
result = result + i + j + k;
}
}
}
$(status_div).text('calculation done');
}
// Assign events to buttons
$('#do').on('click', function () {
$('#status').text('calculating....');
long_running('#status');
});
$('#do_ok').on('click', function () {
$('#status_ok').text('calculating....');
// This works on IE8. Works in Chrome
// Does NOT work in FireFox 25 with timeout =0 or =1
// DOES work in FF if you change timeout from 0 to 500
window.setTimeout(function (){ long_running('#status_ok') }, 0);
});