41 votes

Appel de fonction asynchrone jQuery, pas de demande AJAX

Cela semble idiot, mais je n'arrive pas à trouver comment faire un appel de fonction asynchrone avec jQuery qui n'implique pas une requête côté serveur. J'ai une fonction lente qui itère à travers de nombreux éléments du DOM, et je veux que le navigateur ne se fige pas pendant l'exécution de cette fonction. Je veux afficher un petit indicateur avant que la fonction lente ne soit appelée, 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 fige, puis slowFunctionCall est terminé.

Edit : J'ai utilisé La réponse de Vivin et plus particulièrement le Lien vers 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 permet d'afficher et de masquer l'indicateur et de ne pas geler le navigateur. Vous pouvez voir les éléments du DOM être cachés pendant qu'il fonctionne, ce qui est ce que je voulais.

30voto

Vivin Paliath Points 40975

Javascript fonctionne sur un seul fil d'exécution et, par conséquent, si vous avez une fonction lente, il sera bloquer tout le reste.

UPDATE

Cela fera une partie de ce que vous voulez, mais gardez à l'esprit qu'ils ne sont pas largement soutenu supportés dans IE (je pense qu'ils le seront dans IE10).

Quelques ressources sur les travailleurs Web :

Voici quelques ressources sur la façon de réaliser le multithreading sans les web workers. Il est important de noter que ce n'est pas du "vrai" multithreading :

6voto

slifty Points 2303

J'allais suggérer de regarder un délai d'attente mais hélas. Ce billet de John Resig (de jQuery) explique un peu comment JavaScript gère son fil unique. http://ejohn.org/blog/how-javascript-timers-work/

Cet article explique également : "Une chose à retenir lors de l'exécution asynchrone de fonctions en JavaScript, c'est que toutes les autres exécutions JavaScript de 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 cela peut causer 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 qui s'exécute longtemps va en fait "bloquer" le navigateur pour l'utilisateur. Il en va de même pour les appels de fonctions synchrones."

Cela dit, il est possible de simuler vous-même un appel de fonction asynchrone en fractionnant la boucle que vous effectuez en un plus petit morceau 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)

6voto

nwellcome Points 1564

Vous pourriez vouloir les travailleurs du web !

Edit : Je suis surpris par le nombre de personnes qui se sont empressées de dire "ce n'est pas possible", alors je vais développer. Les Web Workers font partie de la Spécifications HTML 5 . Ils vous permettent de créer 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ées par le biais d'événements.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X