Вызов функции jQuery после завершения .each()

В jQuery возможно ли вызывать обратный вызов или инициировать событие после вызова .each() (или любого другого типа итеративного обратного вызова) имеет завершено.

Например, я хотел бы, чтобы это "выцветание и удаление" завершилось

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

прежде чем делать некоторые вычисления и вставлять новые элементы после $(parentSelect). Мои вычисления неверны, если существующие элементы все еще видны jQuery и спят/задерживают какое-то произвольное количество времени (200 для каждого элемента), в лучшем случае, как хрупкое решение.

Я могу легко .bind() создать необходимую логику для обратного вызова события, но я не уверен, как чисто вызывать .trigger() после завершения вышеперечисленной итерации. Очевидно, я не могу вызвать триггер внутри итерации, поскольку он срабатывал бы несколько раз.

В случае $.each() я рассмотрел вопрос о добавлении чего-то в конец аргумента данных (который я бы искал вручную в теле итерации), но я бы не хотел, чтобы меня это принудили, поэтому я был надеясь, что есть еще один элегантный способ контролировать поток в отношении итеративных обратных вызовов.

Ответ 1

Альтернатива @tv answer:

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

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

Обратите внимание, что .each() сам синхронный — оператор, следующий за вызовом .each(), будет выполнен только после завершения вызова .each(). Однако асинхронные операции, начатые в .each(), будут продолжены по-своему. Это проблема здесь: призывы к стиранию элементов - это анимация с таймером, и они продолжаются в своем собственном темпе.

Таким образом, решение, приведенное выше, отслеживает, сколько элементов исчезает. Каждый вызов .fadeOut() получает обратный вызов завершения. Когда обратный вызов замечает, что он учитывал все исходные элементы, некоторые последующие действия могут быть приняты с уверенностью, что все замирание закончилось.

Это четырехлетний ответ (на данный момент в 2014 году). Современный способ сделать это, вероятно, потребует использования механизма отсрочки/обещания, хотя выше это просто и должно работать нормально.

Ответ 2

Хорошо, это может быть немного после факта, но .promise() также должен добиться того, что вам нужно.

Обещающая документация

Пример из проекта, над которым я работаю:

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

:)

Ответ 3

Возможно, поздно, но я думаю, что этот код работает...

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

Ответ 4

JavaScript работает синхронно, поэтому все, что вы разместите после each(), не будет выполняться до завершения each().

Рассмотрим следующий тест:

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

Когда вызывается предупреждение, счетчик будет равен 1000000, поскольку предупреждение не будет выполняться до тех пор, пока не будет выполнено each().

Ответ 5

Я нашел много ответов, касающихся массивов, но не с объектом json. Мое решение состояло в том, чтобы просто перебирать объект один раз, увеличивая счетчик, а затем, итерации через объект для выполнения кода, вы можете увеличить второй счетчик. Затем вы просто сравниваете два счетчика и получаете свое решение. Я знаю, что это немного неуклюже, но пока я не нашел более элегантного решения. Это мой пример кода:

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
}

Как я уже сказал, это не самый элегантный, но он работает, и он работает хорошо, и я пока не нашел лучшего решения.

Cheers, В JP

Ответ 6

Если вы готовы сделать это несколько шагов, это может сработать. Однако это зависит от анимации, заканчивающейся в порядке. Я не думаю, что это должно быть проблемой.

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

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

Ответ 7

как насчет

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

Ответ 8

Вы должны поставить в очередь остальную часть вашего запроса, чтобы он работал.

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

Ответ 9

Я встречаюсь с той же проблемой, и я решил с решением, как следующий код:

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

});

Решение решает следующую проблему: синхронизировать асинхронные операции, запущенные в итерации .each(), используя объект "Отложен".