Invoking a jQuery function after .each() has completed

JqueryEach

Jquery Problem Overview


In jQuery, is it possible to invoke a callback or trigger an event after an invocation of .each() (or any other type of iterative callback) has completed.

For example, I would like this "fade and remove" to complete

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

before doing some calculations and inserting new elements after the $(parentSelect). My calculations are incorrect if the existing elements are still visible to jQuery and sleeping/delaying some arbitrary amount of time (200 for each element) seems like a brittle solution at best.

I can easily .bind() the necessary logic to an event callback but I'm not sure how to cleanly invoke the .trigger() after the above iteration has completed. Obviously, I can't invoke the trigger inside the iteration as it would fire multiple times.

In the case of $.each(), I've considered adding something to the end of the data argument (that I'd manually look for in the iteration body) but I'd hate to be forced to that so I was hoping there was some other elegant way to control the flow with respect to iterative callbacks.

Jquery Solutions


Solution 1 - Jquery

It's probably to late but i think this code work...

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

Solution 2 - Jquery

An alternative to @tv's answer:

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

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

Note that .each() itself is synchronous — the statement that follows the call to .each() will be executed only after the .each() call is complete. However, asynchronous operations started in the .each() iteration will of course continue on in their own way. That's the issue here: the calls to fade the elements are timer-driven animations, and those continue at their own pace.

The solution above, therefore, keeps track of how many elements are being faded. Each call to .fadeOut() gets a completion callback. When the callback notices that it's counted through all of the original elements involved, some subsequent action can be taken with confidence that all of the fading has finished.

This is a four-year-old answer (at this point in 2014). A modern way to do this would probably involve using the Deferred/Promise mechanism, though the above is simple and should work just fine.

Solution 3 - Jquery

Ok, this might be a little after the fact, but .promise() should also achieve what you're after.

Promise documentation

An example from a project i'm working on:

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

:)

Solution 4 - Jquery

JavaScript runs synchronously, so whatever you place after each() will not run until each() is complete.

Consider the following test:

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);

When the alert is called, count will equal 1000000 because the alert won't run until each() is done.

Solution 5 - Jquery

I'm using something like this:

$.when(
           $.each(yourArray, function (key, value) {
                // Do Something in loop here
            })
          ).then(function () {
               // After loop ends.
          });

Solution 6 - Jquery

I found a lot of responses dealing with arrays but not with a json object. My solution was simply to iterate through the object once while incrementing a counter and then when iterating through the object to perform your code you can increment a second counter. Then you simply compare the two counters together and get your solution. I know it's a little clunky but I haven't found a more elegant solution so far. This is my example 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
}

Like I said, it's not the most elegant, but it works and it works well and I haven't found a better solution just yet.

Cheers, JP

Solution 7 - Jquery

If you're willing to make it a couple of steps, this might work. It's dependent on the animations finishing in order, though. I don't think that should be a problem.

var elems = $(parentSelect).nextAll();
var lastID = elems.length - 1;

elems.each( function(i) {
    $(this).fadeOut(200, function() { 
        $(this).remove(); 
        if (i == lastID) {
           doMyThing();
        }
    });
});

Solution 8 - Jquery

what about

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

Solution 9 - Jquery

You have to queue the rest of your request for it to work.

var elems = $(parentSelect).nextAll();
var lastID = elems.length - 1;

elems.each( function(i) {
    $(this).fadeOut(200, function() { 
        $(this).remove(); 
        if (i == lastID) {
          	$j(this).queue("fx",function(){ doMyThing;});
        }
    });
});

Solution 10 - Jquery

I meet the same problem and I solved with a solution like the following code:

var drfs = new Array();
var external = $.Deferred();
drfs.push(external.promise());

$('itemSelector').each( function() {
	//initialize the context for each cycle
	var t = this; // optional
	var internal = $.Deferred();

	// after the previous deferred operation has been resolved
    drfs.pop().then( function() {
	
		// do stuff of the cycle, optionally using t as this
        var result; //boolean set by the stuff

		if ( result ) {
			internal.resolve();
		} else {
			internal.reject();
		}
	}
	drfs.push(internal.promise());
});

external.resolve("done");

$.when(drfs).then( function() {
	// after all each are resolved

});

The solution solves the following problem: to synchronize the asynchronous operations started in the .each() iteration, using Deferred object.

Solution 11 - Jquery

Maybe a late response but there is a package to handle this https://github.com/ACFBentveld/Await

 var myObject = { // or your array
		1 : 'My first item',
		2 : 'My second item',
		3 : 'My third item'
	}

	Await.each(myObject, function(key, value){
		 //your logic here
	});
	
	Await.done(function(){
	    console.log('The loop is completely done');
	});

Solution 12 - Jquery

var count = $(parentSelect).length;

$(parentSelect).each(function () {

   //do you task here

   if (!--count) {

     //do the cleanup task

     alert ('Reached the end of each loop')
   }

});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLuther BakerView Question on Stackoverflow
Solution 1 - JquerySébastien GRAVIERView Answer on Stackoverflow
Solution 2 - JqueryPointyView Answer on Stackoverflow
Solution 3 - JquerynbspView Answer on Stackoverflow
Solution 4 - Jqueryuser113716View Answer on Stackoverflow
Solution 5 - JquerySoftmixtView Answer on Stackoverflow
Solution 6 - JqueryJimPView Answer on Stackoverflow
Solution 7 - JquerytvanfossonView Answer on Stackoverflow
Solution 8 - JqueryMark SchultheissView Answer on Stackoverflow
Solution 9 - JqueryElyx0View Answer on Stackoverflow
Solution 10 - JqueryRoberto AGOSTINOView Answer on Stackoverflow
Solution 11 - JqueryWim PruiksmaView Answer on Stackoverflow
Solution 12 - JqueryVikram BhatiaView Answer on Stackoverflow