Вызов функции jQuery после завершения .each () - PullRequest
176 голосов
/ 01 марта 2010

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

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

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

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

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

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

Ответы [ 10 ]

159 голосов
/ 20 января 2012

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

$blocks.each(function(i, elm) {
 $(elm).fadeOut(200, function() {
  $(elm).remove();
 });
}).promise().done( function(){ alert("All was done"); } );
156 голосов
/ 01 марта 2010

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

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

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

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

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

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

156 голосов
/ 21 декабря 2011

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

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

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

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

:)

22 голосов
/ 01 марта 2010

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 голосов
/ 01 марта 2011

Я нашел много ответов, связанных с массивами, но не с объектом 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

1 голос
/ 01 марта 2010

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

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

elems.each( function(i) {
    $(this).fadeOut(200, function() { 
        $(this).remove(); 
        if (i == lastID) {
           doMyThing();
        }
    });
});
0 голосов
/ 21 июня 2018

Возможно поздний ответ, но есть пакет, чтобы справиться с этим 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');
    });
0 голосов
/ 05 марта 2017

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

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 (), с использованием отложенного объекта.

0 голосов
/ 21 декабря 2010

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

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;});
        }
    });
});
0 голосов
/ 01 марта 2010

а как же

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