190 votes

Appel d'une fonction jQuery après la fin de .each()

Dans jQuery, est-il possible de invoquer un callback o déclencher un événement après une invocation de .each() (ou tout autre type de callback itératif) dispose de terminé .

Par exemple, je voudrais que ce "fondu-enchaîné" se termine

$(parentSelect).nextAll().fadeOut(200, function() {
    $(this).remove();
});

avant de faire quelques calculs et d'insérer nouveau après les éléments $(parentSelect) . Mes calculs sont incorrects si les éléments existants sont toujours visibles pour jQuery et la mise en sommeil/retardement d'une quantité arbitraire de temps (200 pour chaque élément) semble être une solution fragile au mieux.

Je peux facilement .bind() la logique nécessaire à un rappel d'événement, mais je ne suis pas sûr de savoir comment invoquer proprement l'option .trigger() après que l'itération ci-dessus ait terminé . Évidemment, je ne peux pas invoquer le déclencheur à l'intérieur de l'itération car il se déclencherait plusieurs fois.

Dans le cas de $.each() J'ai envisagé d'ajouter quelque chose à la fin de l'argument data (que je rechercherais manuellement dans le corps de l'itération) mais je ne voudrais pas être obligé de le faire. J'espérais donc qu'il existait un autre moyen élégant de contrôler le flux en ce qui concerne les rappels itératifs.

1 votes

Ai-je bien compris que ce n'est pas tant le ".each()" lui-même que vous voulez terminer, mais plutôt toutes les animations lancées par l'appel ".each()" ?

0 votes

C'est un bon point. Pour cette question particulière, oui, je suis principalement préoccupé par l'achèvement de ".each()" lui-même ... mais je pense que vous soulevez une autre question viable.

0 votes

Il existe un paquet léger pour cela github.com/ACFBentveld/Await

188voto

C'est probablement trop tard mais je pense que ce code fonctionne...

$blocks.each(function(i, elm) {
 $(elm).fadeOut(200, function() {
  $(elm).remove();
 });
}).promise().done( function(){ alert("All was done"); } );

1 votes

Depuis quand each renvoie une promesse ? veuillez vérifier : api.jquery.com/each

169voto

Pointy Points 172438

Une alternative à la réponse de @tv :

var elems = $(parentSelect).nextAll(), count = elems.length;

elems.each( function(i) {
  $(this).fadeOut(200, function() { 
    $(this).remove(); 
    if (!--count) doMyThing();
  });
});

Notez que .each() elle-même est synchrone - la déclaration qui suit l'appel à .each() ne sera exécuté qu'après la .each() l'appel est terminé. Cependant, les opérations asynchrones a commencé dans le .each() itération continueront bien sûr à leur manière. C'est là que le bât blesse : les appels au fondu des éléments sont des animations commandées par un minuteur, et celles-ci se poursuivent à leur propre rythme.

La solution ci-dessus permet donc de savoir combien d'éléments sont en cours d'effacement. Chaque appel à .fadeOut() obtient un rappel d'achèvement. Lorsque le rappel constate qu'il a compté tous les éléments d'origine concernés, une action ultérieure peut être entreprise avec la certitude que le fondu est terminé.

Il s'agit d'une réponse vieille de quatre ans (à ce stade en 2014). Une façon moderne de le faire impliquerait probablement l'utilisation du mécanisme Deferred/Promise, bien que la réponse ci-dessus soit simple et devrait fonctionner parfaitement.

4 votes

J'aime ce changement, bien que je ferais la comparaison à 0 au lieu de la négation logique (--count == 0) puisque vous comptez à rebours. Pour moi, cela rend l'intention plus claire, bien que cela ait le même effet.

0 votes

Il s'avère que votre commentaire initial sur la question était juste. Je m'interrogeais sur .each() et je pensais que c'était ce que je voulais, mais comme vous, tvanfosson et maintenant Patrick l'avez souligné, c'est le fadeOut final qui m'intéressait. Je pense que nous sommes tous d'accord pour dire que votre exemple (comptage au lieu d'index) est probablement le plus sûr.

0 votes

Que voulez-vous dire par "trop compliqué" ? Quelle partie est compliquée ?

156voto

nbsp Points 1313

Ok, c'est peut-être un peu après coup, mais .promise() devrait également vous permettre d'obtenir ce que vous recherchez.

Documentation sur les promesses

Un exemple tiré d'un projet sur lequel je travaille :

$( '.panel' )
    .fadeOut( 'slow')
    .promise()
    .done( function() {
        $( '#' + target_panel ).fadeIn( 'slow', function() {});
    });

:)

12 votes

.promise() n'est pas disponible sur .each()

28voto

user113716 Points 143363

JavaScript s'exécute de manière synchrone, donc tout ce que vous placez après each() ne sera pas exécuté avant que each() est complet.

Considérons le test suivant :

var count = 0;
var array = [];

// populate an array with 1,000,000 entries
for(var i = 0; i < 1000000; i++) {
    array.push(i);
}

// use each to iterate over the array, incrementing count each time
$.each(array, function() {
    count++
});

// the alert won't get called until the 'each' is done
//      as evidenced by the value of count
alert(count);

Lorsque l'alerte est appelée, le compte sera égal à 1000000 car l'alerte ne sera pas exécutée avant que each() est fait.

9 votes

Le problème dans l'exemple donné est que le fadeOut est placé dans la file d'attente de l'animation et ne s'exécute pas de manière synchrone. Si vous utilisez un each et programmer chaque animation séparément, vous devez toujours déclencher l'événement après l'animation, et non après la fin de chacune.

3 votes

@tvanfosson - Oui, je sais. Selon ce que Luther veut accomplir, votre solution (et celle de Pointy) semble être la bonne approche. Mais d'après les commentaires de Luther comme... Naïvement, je cherche quelque chose comme elems.each(foo,bar) où foo='fonction appliquée à chaque élément' et bar='callback après la fin de toutes les itérations'. ...il semble insister sur le fait que c'est un... each() problème. C'est pourquoi j'ai lancé cette réponse dans le mélange.

0 votes

Vous avez raison, Patrick. J'ai (mal) compris que .each() programmait ou mettait en file d'attente mes callbacks. Je pense que l'invocation de fadeOut m'a indirectement conduit à cette conclusion. tvanfosson et Pointy ont tous deux fourni des réponses au problème de fadeOut (ce que je cherchais vraiment) mais ton message corrige un peu ma compréhension. Votre réponse est en fait celle qui répond le mieux à la question originale, tandis que Pointy et tvanfosson ont répondu à la question que j'essayais de poser. J'aimerais pouvoir choisir deux réponses. Merci d'avoir "ajouté cette réponse au mélange" :)

5voto

JimP Points 551

J'ai trouvé beaucoup de réponses traitant des tableaux mais pas d'un objet json. Ma solution était simplement d'itérer à travers l'objet une fois en incrémentant un compteur et ensuite, lorsque vous itérez à travers l'objet pour exécuter votre code, vous pouvez incrémenter un deuxième compteur. Ensuite, vous comparez simplement les deux compteurs ensemble et vous obtenez votre solution. Je sais que c'est un peu lourd, mais je n'ai pas trouvé de solution plus élégante jusqu'à présent. Voici mon exemple de code :

var flag1 = flag2 = 0;

$.each( object, function ( i, v ) { flag1++; });

$.each( object, function ( ky, val ) {

     /*
        Your code here
     */
     flag2++;
});

if(flag1 === flag2) {
   your function to call at the end of the iteration
}

Comme je l'ai dit, ce n'est pas le plus élégant, mais ça marche et ça marche bien et je n'ai pas encore trouvé de meilleure solution.

Santé, JP

1 votes

Non, ça ne marche pas. Il déclenche tous les .each's et ensuite le truc final et tout s'exécute en même temps. Essayez de mettre un setTimeout sur vos .each's et vous verrez que le flag1===flag2 est exécuté immédiatement.

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