18 oct 2009

Ajax Queue avec jQuery

Développement 14 Comments

En travaillant sur un nouvel outil ce week-end, je me suis rendu compte que l’utilisation des queues en Ajax avec jQuery n’était pas natif.
Il existe quelques pistes à ces adresses, mais rien de vraiment pratique :

Puis j’ai trouvé une version plus intéressante de celui-ci dans le plugin [jQuery Autocomplete|http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/|en], néanmoins, même si la fonction ABORT fonctionne correctement, mes appels en queue ne fonctionnaient pas.
J’en ai donc légèrement modifié les sources, en ajoutant un mode DEQUEUE me permettant de lancer la liste d’attente en fin de boucle par exemple.

Grâce à l’utilisation de la variable PORT, on peux ainsi contrôler indépendamment plusieurs queues, ou annuler des requêtes spécifiques, le tout en utilisant la fonction Ajax de jQuery.

Le plugin

/**
 * Ajax Queue Plugin
 */
(function($) {
    var ajax = $.ajax;
    var pendingRequests = {};
 
    $.ajax = function(settings) {
        settings = jQuery.extend(
            settings, 
            jQuery.extend(
                {}, 
                jQuery.ajaxSettings, 
                settings
            )
        );	
        var port = settings.port;
 
        switch(settings.mode) {
            case "abort": 
                if ( pendingRequests[port] ) {
                    pendingRequests[port].abort();
                }
                return pendingRequests[port] = ajax.apply(this, arguments);
 
            case "queue": 
                var _old = settings.complete;
                settings.complete = function(){
                    if ( _old )
                        _old.apply( this, arguments );
                    jQuery([ajax]).dequeue("ajax" + port );
                };
 
                jQuery([ ajax ]).queue("ajax" + port, function(){
                    ajax( settings );
                });
                return;
 
            case "dequeue": 
                jQuery([ajax]).dequeue("ajax" + port );
 
                if(jQuery.isFunction(settings.complete))
                    settings.complete(settings);
 
                return;
        }
 
        return ajax.apply(this, arguments);
    };
})(jQuery);

Utilisation de la méthode ABORT

$input.bind('keyup', function(event) {
    if($searchTimeout != undefined) {
        clearTimeout($searchTimeout);
    }
 
    $searchTimeout = setTimeout(function() {
        $searchTimeout = undefined;
 
        var inputVal = $input.val().replace(/^\s+/g,'').replace(/\s+$/g,'');
 
        $.ajax({
            mode    : 'abort',
            port    : 'ajaxSuggestions',
            type    : 'POST',
            url     : 'test.php',
            data    : { 'keywords': inputVal },
            success : function(msg) {
                alert(msg);
            }
        });
    }, 500);
});

Cette méthode est plus qu’intéressante dans le cas par exemple ou l’on souhaite afficher des informations au fur et à mesure que l’utilisateur écrit une information. Les requêtes précédentes utilisant le même port sont annulées au profit de la nouvelle requête, évitant un engorgement des informations et des erreurs dans le cas ou une requête ultérieure soit plus rapide que les précédentes.

Utilisation de la méthode QUEUE

for( i = 0 ; i <= 10 ; i++ ) {
    $.ajax({
        mode: 'queue',
        port: 'ajaxWhois',
        type: 'POST',
        url: 'test.php',
        data: { 'increment': i },
        success: function(msg){
            alert(msg);
        }
    });
}
 
$.ajax({ mode: 'dequeue', port: 'ajaxWhois' });

La méthode queue permet de boucler facilement sur plusieurs requêtes et ne les lancer qu’au moment choisit. Les requêtes ainsi lancées, le sont les unes à la suite des autres, évitant un engorgement des données reçues.

14 Responses to “Ajax Queue avec jQuery”

  1. Mathieu Delestre says:

    Après avoir essayer de travailler avec le plugin, j’ai constaté que « abort » ne servait pas à grand chose … en effet pendingRequests ne contient que la référence au dernier appel en mode « abort »…
    J’ai donc opté pour une solution plus radicale :

    case "abort":
    jQuery(ajax).queue("ajax" + port, );
    settings.mode='queue';
    $.ajax(settings);
    jQuery(ajax).dequeue("ajax" + port);
    return;

    Si je suis en mode « abort » alors :
    1. Je vide la pile (en passant un tableau vide)
    2. Je passe en mode « queue »
    3. J’ajoute l’appel à la pile fraichement remise à zéro
    4. Et je dépile la file d’attente

    Pour mon test, j’empile environ 200 appel ajax à enchainer, je les lances. A n’importe quel moment je peux lancer un abort.

    Avec la version initiale je voyais simplement passer ma requête en mode abort au milieu du reste

    Avec la version modifié, la dernière requête en cours termine son exécution, ma requête d’interruption est lancé immédiatement après. Une ceci fait, la pile étant vide plus rien ne se passe tant que je n’ai pas relancé un séquence.

    En espérant que cela puisse aider qui que ce soit.

    • Anthor says:

      En effet les deux n’ont aucun lien entre eux ^^
      Ni dans ma version ni dans celle de base, ce qui est expliqué dans le billet. La méthode abort permet de fermer un port pour passer une autre requête. Et la méthode apply applique les nouvelles données.

      Il est vrai qu’une fonction clearQueue serait un plus, mais ce n’est pas un abort. L’abort termine toute connexion active. Vider une queue ne signifie pas forcement bloquer les requêtes en cours.

  2. Mathieu Delestre says:

    Je viens de remarquer un effet de bord si on enchaine un abort avec une mise en pile … les deux requêtes sont lancé en même temps.

    Le principe de lancer une requête ajax pour vider la pile (même si on ne souhaite pas lancer réellement de requête) ne me plaisant pas je me suis tourné vers le plugin ajaxq disponible sur Google Code : http://code.google.com/p/jquery-ajaxq/

    Au lieu d’appeler $.ajax(settings), on appel $.ajaxq(nomDeLaPile, settings)

    Le plugin tourne très bien et parfaitement remplis son rôle sur l’ensemble de mes tests

  3. Aurélien says:

    Bonjour,
    J’aimerais déclencher un évènement une fois que la pile a été entièrement exécutée. En regardant la source, je vois qu’on étend simplement $.ajax pour modifier son comportement, donc en toute logique, je devrais pouvoir utiliser l’évènement natif « complete ». Mais ça ne fonctionne pas…
    Comment pourrais-je procéder ?
    Merci d’avance!

    $.ajax({
        mode: 'dequeue',
        port: 'listeappels',
        complete : function()
        {
            alert('Pile vide');
        }
    });
  4. LoLo says:

    Salut,

    Merci pour le source modifié de l’autocomplete, je ne comprenais pas pourquoi le mode queue ne fonctionnait pas ….

    C’est chose reglée :)

    Je confirme par contre le propos d’Aurélien, « complete » est appellé avant la fin du traitement de la pile

    c’est dommage :)

  5. cris says:

    Hello,

    J’ai un soucis lorsque j’execute ton code d’un manière particulière, je m’explique
    j’ai une page statique qui permet de charger et remplacer à la volée une portion de code
    donc en gros un bete appel $.ajax qui lui meme fait un deuxieme appel $.ajax

    le premier appel est bien réalisé, mais dans le second, là rien, j’ai essayé avec un PORT aléatoire pour bien différencier les 2 queues mais jamais aucun appel à la deuxieme queue.

    pour bien vérifier que c’est au niveau des queues qu’il y a un soucis, j’ai simplement supprimé ton extend et là aucun soucis, les appels ajax sont bien réalisés.

    Une idée?

    Merci d’avance

    cris

    • Anthor says:

      A chaud pas d’idées, mais je t’envoie un email que tu puisses m’envoyer une portion de code pour tester.

    • cris says:

      ok j’attend ton mail ;-)

Leave a Reply