Préface:
REMARQUE IMPORTANTE: même si c'est plus upvoted et accepté, a accepté de répondre par @staticsan en fait n'est PAS CORRECT! - voir David Mulder commentaire pour expliquer pourquoi.
Certains des autres réponses sont correctes butdon pas vraiment d'illustrer ce que le problème est résolu, j'ai donc créé cette réponse de présenter en détail l'illustration.
En tant que tel, je suis de poster une présentation détaillée de ce que le navigateur ne et comment l'utilisation de l' setTimeout()
aide. Il semble assez long, mais est en fait très simple et direct, j'ai juste fait qu'il est très détaillé.
Mise à JOUR: j'ai fait un JSFiddle pour vivre, de démontrer l'explication ci-dessous: http://jsfiddle.net/C2YBE/31/ . Beaucoup de choses à @ThangChung pour aider à kickstart.
UPDATE2: Juste au cas où JSFiddle site web meurt, ou supprime le code, j'ai ajouté le code pour cette réponse à la fin.
DÉTAILS:
Imaginez une application web avec un "faire quelque chose" bouton et un résultat div.
L' onClick
gestionnaire pour "faire quelque chose" bouton appelle une fonction "LongCalc()", qui n'a que 2 choses:
Fait un très long temps de calcul (dire prend 3 min)
Imprime les résultats de calcul dans le résultat div.
Maintenant, à vos utilisateurs de commencer à tester cela, cliquez sur "faire quelque chose", et la page reste là, à faire semblant de rien pendant 3 minutes, ils s'impatientent, cliquez sur le bouton de nouveau, attendre 1 min, rien ne se passe, cliquez sur le bouton nouveau...
Le problème, c'est évident - vous voulez un "État" de la DIV, qui montre ce qu'il se passe. Nous allons voir comment cela fonctionne.
Si vous ajoutez un "État" de la DIV (initialement vide), et de modifier l' onlcick
gestionnaire (fonction LongCalc()
) à faire 4 choses:
Remplir le statut de "Calcul... peut prendre environ 3 minutes" en état de DIV
Fait un très long temps de calcul (dire prend 3 min)
Imprime les résultats de calcul dans le résultat div.
Remplir le statut de "Calcul" en état de DIV
Et, vous heureux de donner de l'application pour les utilisateurs à re-test.
Ils reviennent pour vous à la recherche très en colère. Et d'expliquer que lorsqu'ils ont cliqué sur le bouton, le Statut DIV ne l'ai jamais mis à jour avec "Calcul..."!!!
Vous vous grattez vous la tête, demandez autour de vous sur StackOverflow (ou de lire des docs ou google), et de réaliser le problème:
Le navigateur place l'ensemble de ses "TODO" tâches (à la fois de l'INTERFACE utilisateur des tâches et des commandes JavaScript) résultant d'événements en une seule file d'attente. Et malheureusement, re-dessiner le "Statut" des DIV avec la nouvelle "le Calcul..." la valeur est séparée TODO qui va jusqu'au bout de la file d'attente!
Voici une répartition des événements au cours de l'utilisateur de votre test, le contenu de la file d'attente après chaque événement:
- File d'attente:
[Empty]
- Événement: Cliquez sur le bouton. La file d'attente après l'événement:
[Execute OnClick handler(lines 1-4)]
- Événement: Exécution de la première ligne dans le gestionnaire Onclick (par exemple, changement de Statut DIV vaue). La file d'attente après l'événement:
[Execute OnClick handler(lines 2-4), re-draw Status DIV with new "Calculating" value]
. Veuillez noter que le DOM changements se produire instantanément, à re-dessiner le correspondant de l'élément DOM vous avez besoin d'un nouvel événement, déclenchée par le DOM changement, qui est allé à la fin de la file d'attente.
-
PROBLÈME!!!!!! PROBLÈME!!!!!! Détails expliqués ci-dessous.
- Événement: Exécution de la deuxième ligne du gestionnaire (calulation). File d'attente après:
[Execute OnClick handler(lines 3-4), re-draw Status DIV with "Calculating" value]
.
- Événement: Exécuter 3d ligne dans le gestionnaire (remplir résultat DIV). File d'attente après:
[Execute OnClick handler(line 4), re-draw Status DIV with "Calculating" value, re-draw result DIV with result]
.
- Événement: Exécution de la ligne 4 dans gestionnaire (remplir le statut de DIV avec "TERMINÉ"). File d'attente:
[Execute OnClick handler, re-draw Status DIV with "Calculating" value, re-draw result DIV with result; re-draw Status DIV with "DONE" value]
.
- Événement: exécuter implicite
return
de onclick
gestionnaire de sous. Nous prenons la "Exécuter OnClick gestionnaire" de la file d'attente et de lancer l'exécution d'élément suivant dans la file d'attente.
- REMARQUE: comme nous l'avons déjà fini le calcul, à 3 minutes déjà passé pour l'utilisateur. Le re-dessiner événement n'arrive pas encore!!!
- Événement: re-dessiner le Statut de DIV avec "Calcul de la" valeur. Nous ne le re-dessiner et prendre la file d'attente.
- Événement: re-dessiner Résultat DIV avec la valeur du résultat. Nous ne le re-dessiner et prendre la file d'attente.
- Événement: re-dessiner le Statut de DIV avec "Faire" de la valeur. Nous ne le re-dessiner et prendre la file d'attente.
Sharp-eyed les téléspectateurs pourraient même avis "Statut" DIV " avec "Calcul de la" valeur clignotante pour la fraction de microseconde APRÈS LE CALCUL TERMINÉ
Donc, le problème sous-jacent est que la re-tirer de l'événement pour "Statut" DIV est placé dans la file d'attente à la fin, APRÈS la "exécuter la ligne 2" événement qui prend 3 minutes, donc la re-dessiner n'arrive pas jusqu'à ce que, APRÈS le calcul est fait.
Le sauvetage est l' setTimeout()
. Comment peut-il aider? Car en appelant au long de l'exécution de code via setTimeout
, vous avez réellement créer 2 événements: setTimeout
exécution elle-même, et (en raison de 0 timeout), séparé de la file d'attente d'entrée pour le code en cours d'exécution.
Donc, pour résoudre votre problème, vous devez modifier votre onClick
gestionnaire à DEUX états (dans une nouvelle fonction ou juste un bloc à l'intérieur d' onClick
):
Remplir le statut de "Calcul... peut prendre environ 3 minutes" en état de DIV
-
Exécuter setTimeout()
0 délai d'attente et un appel à l' LongCalc()
fonction.
LongCalc()
fonction est presque la même que la dernière fois, mais n'a évidemment pas de "Calcul..." DIV mise à jour en tant que première étape; et au lieu de cela démarre le calcul immédiatement.
Alors, quelle est la séquence des événements et de la file d'attente maintenant?
- File d'attente:
[Empty]
- Événement: Cliquez sur le bouton. La file d'attente après l'événement:
[Execute
OnClickhandler(status update,
setTimeout()call)]
- Événement: Exécution de la première ligne dans le gestionnaire Onclick (par exemple, changement de Statut DIV valeur). La file d'attente après l'événement:
[Execute OnClick handler(which is a setTimeout call), re-draw Status DIV with new "Calculating" value]
.
- Événement: Exécution de la deuxième ligne du gestionnaire (setTimeout appel). File d'attente après:
[re-draw Status DIV with "Calculating" value]
. La file d'attente a rien de nouveau dans pour 0 secondes de plus.
- Événement: l'Alarme de la temporisation s'en va, 0 secondes plus tard. File d'attente après:
[re-draw Status DIV with "Calculating" value, execute LongCalc (lines 1-3)]
.
- Événement: re-dessiner le Statut de DIV avec "Calcul de la" valeur. File d'attente après:
[execute LongCalc (lines 1-3)]
. Veuillez noter que cette ré-attirer l'événement pourrait effectivement se produire AVANT que l'alarme se déclenche, qui fonctionne tout aussi bien.
- ...
Hoorray! Le Statut DIV viens de la mise à jour de "de Calcul..." avant le calcul commencé!!!
Ci-dessous est un exemple de code à partir de la JSFiddle illustrant ces exemples: http://jsfiddle.net/C2YBE/31/ :
Code 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>
Le code JavaScript: (Exécuté sur onDomReady
et peut exiger 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('calclation 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);
});