Menjalankan fungsi jQuery setelah .each () selesai


183

Di jQuery, apakah mungkin untuk memanggil balik atau memicu suatu peristiwa setelah doa .each()(atau jenis lain dari panggilan balik berulang) selesai .

Misalnya, saya ingin ini "memudar dan menghapus" untuk menyelesaikan

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

sebelum melakukan beberapa perhitungan dan memasukkan elemen baru setelah $(parentSelect). Perhitungan saya salah jika elemen yang ada masih terlihat oleh jQuery dan tidur / menunda beberapa waktu yang sewenang-wenang (200 untuk setiap elemen) sepertinya merupakan solusi yang rapuh.

Saya dapat dengan mudah .bind()logika yang diperlukan untuk callback acara, tetapi saya tidak yakin bagaimana memanggil dengan baik .trigger()setelah iterasi di atas selesai . Jelas, saya tidak bisa memanggil pelatuk di dalam iterasi karena akan menyala beberapa kali.

Dalam hal ini $.each(), saya telah mempertimbangkan untuk menambahkan sesuatu pada akhir argumen data (yang secara manual akan saya cari di badan iterasi) tetapi saya benci dipaksa untuk itu, jadi saya berharap ada beberapa yang elegan lainnya. cara untuk mengontrol aliran sehubungan dengan panggilan balik berulang.


1
Apakah saya benar mengerti bahwa itu bukan ".each ()" itu sendiri yang ingin Anda selesaikan, melainkan semua animasi yang diluncurkan oleh panggilan ".each ()"?
Pointy

Itu poin yang bagus. Dengan pertanyaan khusus ini, ya, saya terutama khawatir tentang penyelesaian ".each ()" itu sendiri ... tapi saya pikir Anda mengajukan pertanyaan yang layak.
Luther Baker

Jawaban:


161

Alternatif jawaban @ tv:

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

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

Perhatikan bahwa .each()itu sendiri sinkron - pernyataan yang mengikuti panggilan ke .each()akan dieksekusi hanya setelah .each()panggilan selesai. Namun, operasi asinkron yang dimulai dalam .each()iterasi tentu saja akan berlanjut dengan caranya sendiri. Itulah masalahnya di sini: panggilan untuk memudar elemen adalah animasi yang digerakkan oleh timer, dan mereka melanjutkan dengan langkah mereka sendiri.

Solusi di atas, oleh karena itu, melacak berapa banyak elemen yang sedang memudar. Setiap panggilan untuk .fadeOut()mendapat panggilan balik selesai. Ketika panggilan balik pemberitahuan itu dihitung melalui semua elemen asli yang terlibat, beberapa tindakan selanjutnya dapat diambil dengan keyakinan bahwa semua fading telah selesai.

Ini adalah jawaban empat tahun (saat ini pada tahun 2014). Cara modern untuk melakukan ini mungkin akan melibatkan menggunakan mekanisme Ditangguhkan / Janji, meskipun di atas sederhana dan harus bekerja dengan baik.


4
Saya suka perubahan ini, meskipun saya akan membuat perbandingan dengan 0 bukannya negasi logis (--count == 0) karena Anda menghitung mundur. Bagi saya itu membuat niat menjadi lebih jelas, meskipun memiliki efek yang sama.
tvanfosson

Ternyata, komentar awal Anda tentang pertanyaan itu tepat. Saya bertanya tentang .each () dan berpikir itulah yang saya inginkan tetapi seperti yang Anda dan tvanfosson dan sekarang patrick telah tunjukkan - itu adalah fadeOut terakhir yang saya benar-benar tertarik. Saya pikir kita semua setuju dengan contoh Anda (menghitung sebagai gantinya indeks) mungkin yang paling aman.
Luther Baker

@ A.DIMO Apa maksudmu, "terlalu rumit"? Bagian mana yang rumit?
Runcing

169

Mungkin terlambat tapi saya pikir kode ini berfungsi ...

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

156

Ok, ini mungkin sedikit setelah fakta, tetapi .promise () juga harus mencapai apa yang Anda cari.

Dokumentasi janji

Contoh dari proyek yang sedang saya kerjakan:

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

:)


8
.promise () tidak tersedia di .each ()
Michael Fever

25

JavaScript berjalan secara sinkron, jadi apa pun yang Anda tempatkan each()tidak akan berjalan sampai each()selesai.

Pertimbangkan tes berikut:

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

Ketika lansiran dipanggil, hitungan akan sama dengan 1000000 karena lansiran tidak akan berjalan sampai each()selesai.


8
Masalah dalam contoh yang diberikan adalah bahwa fadeOut akan dimasukkan ke antrian animasi dan tidak dijalankan secara serempak. Jika Anda menggunakan eachdan menjadwalkan setiap animasi secara terpisah, Anda masih harus memecat acara setelah animasi, bukan masing-masing selesai.
tvanfosson

3
@tvanfosson - Ya, saya tahu. Menurut apa yang ingin dicapai Luther, solusi Anda (dan Pointy) tampaknya merupakan pendekatan yang tepat. Tetapi dari komentar Luther seperti ... Secara naif, saya mencari sesuatu seperti elems.each (foo, bar) di mana foo = 'fungsi diterapkan pada setiap elemen' dan bar = 'callback setelah semua iterasi selesai'. ... dia tampaknya bersikeras bahwa itu adalah each()masalah. Itu sebabnya saya melemparkan jawaban ini ke dalam campuran.
user113716

Anda Patrick benar. Saya (salah) mengerti bahwa .each () sedang menjadwalkan atau mengantri callback saya. Saya pikir memohon fadeOut secara tidak langsung membimbing saya ke kesimpulan itu. tvanfosson dan Pointy sama-sama memberikan jawaban untuk masalah fadeOut (yang memang benar-benar saya cari) tetapi postingan Anda sebenarnya sedikit mengoreksi pemahaman saya. Jawaban Anda sebenarnya menjawab pertanyaan asli sebagaimana dinyatakan saat Pointy dan tvanfosson menjawab pertanyaan yang saya coba tanyakan. Saya berharap saya bisa memilih dua jawaban. Terima kasih telah 'melempar jawaban ini ke dalam campuran' :)
Luther Baker

@ user113716 Saya pikir fungsi di dalam eachtidak dijamin dipanggil sebelumnya alert. Bisakah Anda menjelaskan atau mengarahkan saya ke bacaan tentang ini?
Maciej Jankowski

5

Saya menemukan banyak respons berurusan dengan array tetapi tidak dengan objek json. Solusi saya adalah dengan hanya mengulangi objek sekali saja sambil menambah penghitung dan kemudian saat mengulangi objek untuk melakukan kode Anda, Anda dapat menambah penghitung kedua. Maka Anda cukup membandingkan kedua penghitung bersama dan mendapatkan solusi Anda. Saya tahu ini agak kikuk tapi sejauh ini saya belum menemukan solusi yang lebih elegan. Ini adalah contoh kode saya:

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
}

Seperti yang saya katakan, ini bukan yang paling elegan, tetapi bekerja dan bekerja dengan baik dan saya belum menemukan solusi yang lebih baik.

Cheers, JP


1
Tidak, ini tidak berhasil. Itu menembakkan semua .each dan kemudian hal terakhir dan semuanya berjalan pada saat yang sama. Coba letakkan setTimeout di .each Anda dan Anda akan melihatnya menjalankan flag1 === flag2 segera
Michael Fever

1

Jika Anda ingin membuatnya beberapa langkah, ini mungkin berhasil. Ini tergantung pada animasi yang diselesaikan secara berurutan. Saya tidak berpikir itu harus menjadi masalah.

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

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

Ini akan berhasil - meskipun, bukan apa yang saya harapkan. Secara naif, saya mencari sesuatu seperti elems.each (foo, bar) di mana foo = 'fungsi diterapkan ke setiap elemen' dan bar = 'callback setelah semua iterasi selesai'. Saya akan memberikan pertanyaan sedikit lebih banyak waktu untuk melihat apakah kita menemukan sudut gelap jQuery :)
Luther Baker

AFAIK - Anda harus memicunya ketika animasi "terakhir" selesai dan karena mereka berjalan secara tidak sinkron, Anda harus melakukannya di callback animasi. Saya suka versi @ Pointy dari apa yang saya tulis lebih baik karena tidak memiliki ketergantungan pada pemesanan, bukan karena saya pikir itu akan menjadi masalah.
tvanfosson

0

bagaimana dengan

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

Ini jelas memanggil fungsi saya () sekali (dan memperkenalkan saya ke konsep jQuery lain) tapi apa yang saya cari adalah jaminan 'kapan' itu akan berjalan - tidak hanya itu akan berjalan sekali. Dan, seperti yang Patrick katakan, bagian itu ternyata cukup mudah. Apa yang saya benar-benar cari adalah cara untuk memanggil sesuatu setelah logika fadeOut terakhir berjalan yang, saya rasa .one () tidak menjamin.
Luther Baker

Saya pikir rantai melakukan itu - sejak bagian pertama berjalan sebelum bagian terakhir.
Mark Schultheiss

0

Anda harus mengantri sisa permintaan Anda agar bisa berfungsi.

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

Saya menemukan masalah yang sama dan saya menyelesaikannya dengan solusi seperti kode berikut:

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

});

Solusinya memecahkan masalah berikut: untuk menyinkronkan operasi asinkron dimulai pada iterasi .each (), menggunakan objek Ditangguhkan.


Anda kehilangan braket penutup) sebelumnya: drfs.push (internal.promise ()); setidaknya saya pikir itu sebelum ..
Michael Fever

0

Mungkin tanggapan terlambat tetapi ada paket untuk menangani ini https://github.com/ACFBentveld/Aunggu

 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

Saya menggunakan sesuatu seperti ini:

$.when(
           $.each(yourArray, function (key, value) {
                // Do Something in loop here
            })
          ).then(function () {
               // After loop ends.
          });
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.