web-dev-qa-db-fra.com

jQuery asynchronous function call, no AJAX request

Cela semble idiot, mais je ne trouve pas comment faire un appel de fonction asynchrone avec jQuery qui n'implique pas de requête côté serveur. J'ai une fonction lente qui itère à travers de nombreux éléments DOM, et je veux que le navigateur ne se fige pas pendant que cette fonction est en cours d'exécution. Je veux afficher un petit indicateur avant d'appeler la fonction lente, puis lorsque la fonction lente revient, je veux masquer l'indicateur. J'ai les éléments suivants:

$('form#filter', parentNode).submit(function() {
  var form = $(this);
  indicator.show();
  var textField = $('input#query', form);
  var query = jQuery.trim(textField.val());
  var re = new RegExp(query, "i");
  slowFunctionCall(); // want this to happen asynchronously; all client-side
  indicator.hide();
  return false;
});

Actuellement, je soumets le formulaire et l'indicateur ne s'affiche pas, le navigateur se bloque, puis slowFunctionCall est terminé.

Edit: J'ai utilisé la réponse de Vivin , en particulier le lien Sitepoint pour obtenir la solution suivante:

var indicator = $('#tagFilter_loading', parentNode);
indicator.hide();
var spans = $('div#filterResults span', parentNode);
var textField = $('input#query', parentNode);
var timer = undefined, processor = undefined;
var i=0, limit=spans.length, busy=false;
var filterTags = function() {
  i = 0;
  if (processor) {
    clearInterval(processor);
  }
  indicator.show();
  processor = setInterval(function() {
    if (!busy) {
      busy = true;
      var query = jQuery.trim(textField.val()).toLowerCase();
      var span = $(spans[i]);
      if ('' == query) {
        span.show();
      } else {
        var tagName = span.attr('rel').toLowerCase();
        if (tagName.indexOf(query) == -1) {
          span.hide();
        }
      }
      if (++i >= limit) {
        clearInterval(processor);
        indicator.hide();
      }
      busy = false;
    }
  }, 1);
};
textField.keyup(function() {
  if (timer) {
    clearTimeout(timer);
  }
  /* Only start filtering after the user has finished typing */
  timer = setTimeout(filterTags, 2000);
});
textField.blur(filterTags);

Cela affiche et masque l'indicateur et ne gèle pas non plus le navigateur. Vous pouvez regarder les éléments DOM cachés pendant que cela fonctionne, c'est ce que j'allais faire.

41
Sarah Vessels

Javascript fonctionne sur un seul thread et donc si vous avez une fonction lente, il va bloquer tout le reste.

[~ # ~] mise à jour [~ # ~]

Cela fera ce que vous voulez, mais gardez à l'esprit qu'ils ne sont pas largement soutenu pris en charge dans IE (je pense qu'ils seront dans IE10).

Quelques ressources sur les travailleurs Web:

Voici quelques ressources sur l'accomplissement du multi-threading sans travailleurs Web. Il est important de noter que ce n'est pas du "vrai" multi-threading:

30
Vivin Paliath

J'allais suggérer de regarder un timeout mais hélas. Cet article de John Resig (de jQuery) explique un peu comment JavaScript gère son thread unique. http://ejohn.org/blog/how-javascript-timers-work/

Cet article explique également: "Une chose à retenir lors de l'exécution de fonctions de manière asynchrone en JavaScript, est que toutes les autres exécutions JavaScript dans la page s'arrêtent jusqu'à ce qu'un appel de fonction soit terminé. C'est ainsi que tous les navigateurs actuels exécutent JavaScript, et peut entraîner de réels problèmes de performances si vous essayez d'appeler trop de choses de manière asynchrone en même temps. Une fonction de longue durée "verrouillera" le navigateur pour l'utilisateur. Il en va de même pour les appels de fonctions synchrones également. "

Cela dit, il est possible que vous puissiez simuler vous-même une fonction asynchrone en cassant la boucle que vous faites en un bloc plus petit et en utilisant setTimeout ().

Par exemple, cette fonction:

// Sync
(function() {
  for(var x = 0; x < 100000; ++x) {console.log(x)}
})()

// ASync
var x = 0;
setTimeout(function() {
   console.log(x++);
   if(x < 100000) setTimeout(arguments.callee, 1);
} ,1)
6
slifty

Vous voudrez peut-être travailleurs Web!

Edit: Je suis surpris du nombre de personnes qui sont passées à "ce n'est pas possible", donc je vais élaborer. Les travailleurs Web font partie de la spécification HTML 5 . Ils vous permettent de générer des threads pour exécuter des scripts en arrière-plan sans bloquer l'interface utilisateur. Ils doivent être des fichiers js externes, sont invoqués par

var worker = new Worker('my_task.js');

et sont communiqués via des événements.

5
nwellcome