序文:
他のいくつかの答えは正しいですが、解決されている問題が実際に何であるかを説明していないので、私はその詳細な図を提示するためにこの答えを作成しました。
そのため、ブラウザの機能と使用方法の詳細なウォークスルーをsetTimeout()
投稿しています。長く見えますが、実際には非常に単純でわかりやすいものです。非常に詳細に作成しました。
更新:私はJSFiddleをライブで作成しました-以下の説明を示します:http://jsfiddle.net/C2YBE/31/。キックスタートを手伝ってくれた@ThangChungに感謝します。
UPDATE2: JSFiddle Webサイトが停止したり、コードが削除されたりした場合に備えて、最後にこの回答にコードを追加しました。
詳細:
「何かをする」ボタンと結果divを備えたWebアプリを想像してみてください。
onClick
「何かをする」ボタンのハンドラーは、2つのことを行う関数「LongCalc()」を呼び出します。
非常に長い計算を行います(たとえば3分かかります)
計算結果を結果divに出力します。
これで、ユーザーはこれをテストし始め、[何かをする]ボタンをクリックすると、ページは3分間何もしないように見えます。落ち着きがなくなり、ボタンをもう一度クリックし、1分待って、何も起こらず、ボタンをもう一度クリックします...
問題は明らかです。何が起こっているかを示す「ステータス」DIVが必要です。それがどのように機能するか見てみましょう。
したがって、「ステータス」DIV(最初は空)を追加し、onclick
ハンドラー(関数LongCalc()
)を変更して次の4つのことを行います。
ステータス「計算中...3分ほどかかる場合があります」をステータスDIVに入力します
非常に長い計算を行います(たとえば3分かかります)
計算結果を結果divに出力します。
ステータス「計算完了」をステータスDIVに入力します
そして、ユーザーにアプリを提供して再テストしてもらいます。
彼らは非常に怒っているようにあなたに戻ってきます。そして、ボタンをクリックしたときに、ステータスDIVが「計算中...」ステータスで更新されなかったことを説明します!!!
あなたは頭をかいて、StackOverflowで質問し(またはドキュメントやグーグルを読んで)、問題に気づきます:
ブラウザは、イベントから生じるすべての「TODO」タスク(UIタスクとJavaScriptコマンドの両方)を単一のキューに配置します。残念ながら、新しい「Calculating ...」値を使用して「Status」DIVを再描画することは、キューの最後に行く別のTODOです。
ユーザーのテスト中のイベントの内訳、各イベント後のキューの内容は次のとおりです。
- 列:
[Empty]
- イベント:ボタンをクリックします。イベント後のキュー:
[Execute OnClick handler(lines 1-4)]
- イベント:OnClickハンドラーの最初の行を実行します(たとえば、ステータスDIV値を変更します)。イベント後のキュー:
[Execute OnClick handler(lines 2-4), re-draw Status DIV with new "Calculating" value]
。DOMの変更は瞬時に行われますが、対応するDOM要素を再描画するには、キューの最後で発生したDOMの変更によってトリガーされる新しいイベントが必要であることに注意してください。
- 問題!!! 問題!!!詳細は以下に説明します。
- イベント:ハンドラーの2行目を実行します(計算)。後のキュー:
[Execute OnClick handler(lines 3-4), re-draw Status DIV with "Calculating" value]
。
- イベント:ハンドラーで3行目を実行します(結果DIVを入力します)。後のキュー:
[Execute OnClick handler(line 4), re-draw Status DIV with "Calculating" value, re-draw result DIV with result]
。
- イベント:ハンドラーの4行目を実行します(ステータスDIVに「DONE」を入力します)。キュー:
[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
「ExecuteOnClickハンドラー」をキューから外し、キューの次のアイテムの実行を開始します。
- 注:計算はすでに終了しているため、ユーザーはすでに3分経過しています。再描画イベントはまだ発生していません!!!
- イベント:「計算中」の値でステータスDIVを再描画します。再描画を行い、それをキューから外します。
- イベント:結果値を使用して結果DIVを再描画します。再描画を行い、それをキューから外します。
- イベント:「完了」値でステータスDIVを再描画します。再描画を行い、それをキューから外します。鋭い目の視聴者は、「計算中」の値がマイクロ秒の何分の1かで点滅している「ステータスDIV」に気付くかもしれません-計算が終了した後
したがって、根本的な問題は、「Status」DIVの再描画イベントが、3分かかる「executeline 2」イベントの後、最後にキューに配置されるため、実際の再描画が行われるまでは発生しないことです。計算が行われた後。
救助に来るsetTimeout()
。それはどのように役立ちますか?を介して長時間実行コードを呼び出すことによりsetTimeout
、実際には2つのイベントが作成されます。setTimeout
実行自体と(タイムアウトが0であるため)、実行中のコードのキューエントリを分離します。
したがって、問題を修正するには、onClick
ハンドラーを2つのステートメント(新しい関数または内のブロック内onClick
)になるように変更します。
ステータス「計算中...3分ほどかかる場合があります」をステータスDIVに入力します
setTimeout()
0タイムアウトとLongCalc()
関数の呼び出しで実行します。
LongCalc()
機能は前回とほぼ同じですが、最初のステップとして「計算中...」ステータスDIVの更新がないことは明らかです。代わりに、すぐに計算を開始します。
では、イベントシーケンスとキューは現在どのようになっていますか?
- 列:
[Empty]
- イベント:ボタンをクリックします。イベント後のキュー:
[Execute OnClick handler(status update, setTimeout() call)]
- イベント:OnClickハンドラーの最初の行を実行します(たとえば、ステータスDIV値を変更します)。イベント後のキュー:
[Execute OnClick handler(which is a setTimeout call), re-draw Status DIV with new "Calculating" value]
。
- イベント:ハンドラーで2行目を実行します(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);
});