在.each()完成后调用jQuery函数

在jQuery中,在调用具有已完成.each()(或任何其他类型的迭代回调)后,是否可以调用回调触发事件

例如,我希望这个“淡入淡出并删除”完成

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

在进行一些计算并在$(parentSelect)之后插入新的元素之前。我的计算是不正确的,如果现有的元素仍然对jQuery可见,并且睡眠/延迟一些任意数量的时间(每个元素200)似乎是一个脆弱的解决方案。

我可以轻松地将ABC0__事件回调的必要逻辑,但我不确定如何在上述迭代已完成之后干净地调用.trigger()。显然,我不能在迭代中调用触发器,因为它会触发多次。

$.each()的情况下,我曾考虑在数据参数的末尾添加一些内容(我会在迭代主体中手动查找),但我不愿意被迫这样做,所以我希望有其他一些优雅的方法来控制迭代回调的流程。

260065 次浏览

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

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.

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.

what about

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

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

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

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() {});
});

:)

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

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.

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

I'm using something like this:

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


$(parentSelect).each(function () {


//do you task here


if (!--count) {


//do the cleanup task


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


});