เรียกใช้ฟังก์ชัน jQuery หลังจาก. each () เสร็จสมบูรณ์


183

ใน jQuery มันเป็นไปได้ที่จะก่อให้เกิดการติดต่อกลับหรือเรียกเหตุการณ์หลังจากการภาวนาของ.each()(หรือชนิดอื่น ๆ ของการเรียกกลับซ้ำ) ได้เสร็จสิ้น

ตัวอย่างเช่นฉันต้องการให้ "จางและลบ" เพื่อให้เสร็จสมบูรณ์

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

ก่อนที่จะทำคำนวณบางและใส่ใหม่$(parentSelect)องค์ประกอบหลังจากที่ การคำนวณของฉันไม่ถูกต้องหากองค์ประกอบที่มีอยู่ยังคงปรากฏให้เห็นสำหรับ jQuery และการนอนหลับ / ล่าช้าบางเวลา (200 สำหรับแต่ละองค์ประกอบ) ดูเหมือนว่าเป็นวิธีการแก้ปัญหาที่เปราะที่สุด

ฉันสามารถ.bind()ตรรกะที่จำเป็นไปยังโทรกลับเหตุการณ์ แต่ผมไม่แน่ใจว่าจะเรียบร้อยเรียก.trigger()หลังจากที่ดังกล่าวข้างต้นซ้ำได้เสร็จสมบูรณ์ เห็นได้ชัดว่าฉันไม่สามารถเรียกใช้ทริกเกอร์ในการทำซ้ำเพราะมันจะยิงหลายครั้ง

ในกรณีของ$.each()ฉันได้พิจารณาการเพิ่มบางอย่างที่จุดสิ้นสุดของ data data (ที่ฉันค้นหาด้วยตนเองในส่วนการทำซ้ำ) แต่ฉันเกลียดที่จะถูกบังคับให้ทำเช่นนั้นดังนั้นฉันจึงหวังว่าจะมีคนอื่นที่สง่างาม วิธีในการควบคุมการไหลของการโทรกลับซ้ำ


1
ฉันเข้าใจอย่างถูกต้องหรือไม่ว่า ".each ()" ตัวเองที่คุณต้องการเสร็จสิ้น แต่เป็นภาพเคลื่อนไหวทั้งหมดที่เปิดใช้โดยการโทร ".each ()" หรือไม่
Pointy

นั่นเป็นจุดที่ดี ด้วยคำถามเฉพาะนี้ใช่ฉันกังวลเป็นส่วนใหญ่เกี่ยวกับความสำเร็จของ ".each ()" เอง ... แต่ฉันคิดว่าคุณจะถามคำถามที่มีประโยชน์อีกข้อ
Luther Baker

มีแพ็คเกจน้ำหนักเบาสำหรับgithub.com/ACFBentveld/Await
Wim Pruiksma

คำตอบ:


161

อีกทางเลือกหนึ่งสำหรับคำตอบของ @ tv:

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) วิธีการที่ทันสมัยในการทำเช่นนี้อาจเกี่ยวข้องกับการใช้กลไก Deferred / Promise ถึงแม้ว่าวิธีการข้างต้นนั้นง่ายและควรทำงานได้ดี


4
ฉันชอบการเปลี่ยนแปลงนี้แม้ว่าฉันจะทำการเปรียบเทียบกับ 0 แทนการปฏิเสธเชิงตรรกะ (--count == 0) เนื่องจากคุณนับถอยหลัง สำหรับฉันมันทำให้เจตนาชัดเจนขึ้นแม้ว่ามันจะมีผลเหมือนกัน
tvanfosson

ตามที่ปรากฎออกมาความคิดเห็นเริ่มต้นของคุณในคำถามนั้นเป็นที่น่าสังเกต ฉันถูกถามเกี่ยวกับ. each () และคิดว่านั่นคือสิ่งที่ฉันต้องการ แต่ในขณะที่คุณและ tvanfosson และตอนนี้ patrick ได้ชี้ให้เห็น - มันเป็น fadeOut สุดท้ายที่ฉันสนใจจริง ๆ ฉันคิดว่าเราทุกคนเห็นด้วยเช่นกัน ของดัชนี) น่าจะปลอดภัยที่สุด
ลูเทอร์เบเกอร์

@ A.DIMO คุณหมายถึงอะไร "ซับซ้อนเกินไป"? ส่วนไหนซับซ้อน?
แหลม

169

อาจจะมาช้า แต่ฉันคิดว่ารหัสนี้ใช้ได้ ...

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

156

ตกลงนี่อาจเป็นเพียงเล็กน้อยหลังจากความจริง แต่. promise () ควรบรรลุสิ่งที่คุณต้องการ

เอกสารสัญญา

ตัวอย่างจากโครงการที่ฉันกำลังทำงาน:

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

:)


8
.promise () ไม่สามารถใช้ได้ใน. each ()
Michael Fever

25

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()จะเสร็จ


8
ปัญหาในตัวอย่างที่กำหนดคือ fadeOut ได้รับการวางในคิวภาพเคลื่อนไหวและไม่ทำงานพร้อมกัน หากคุณใช้eachและกำหนดเวลาภาพเคลื่อนไหวแต่ละรายการแยกกันคุณยังคงต้องดำเนินการเหตุการณ์หลังจากภาพเคลื่อนไหวไม่ใช่แต่ละภาพเคลื่อนไหว
tvanfosson

3
@tvanfosson - ใช่ฉันรู้ ตามที่ลูเทอร์ต้องการที่จะทำให้สำเร็จโซลูชันของคุณ (และของ Pointy) ดูเหมือนจะเป็นแนวทางที่ถูกต้อง แต่จากความคิดเห็นของลูเทอร์เช่น ... อย่างไร้เดียงสาฉันกำลังมองหาบางสิ่งบางอย่างเช่น elems.each (foo, bar) ที่ฟังก์ชัน foo = 'ใช้กับแต่ละองค์ประกอบ' และ bar = 'การโทรกลับหลังจากการทำซ้ำทั้งหมดเสร็จสิ้นแล้ว' ... เขาดูเหมือนจะยืนกรานว่าเป็นeach()ปัญหา นั่นเป็นเหตุผลที่ฉันโยนคำตอบนี้ลงในส่วนผสม
user113716

คุณถูกต้องแพทริค ฉันเข้าใจผิดแล้วว่าแต่ละ. () กำลังกำหนดเวลาหรือรอคิวการโทรกลับของฉัน ฉันคิดว่าการเรียกใช้ fadeOut นำฉันไปสู่ข้อสรุปทางอ้อม tvanfosson และ Pointy ได้ให้คำตอบสำหรับปัญหา fadeOut (ซึ่งเป็นสิ่งที่ฉันเป็นจริงหลังจากนั้น) แต่โพสต์ของคุณแก้ไขความเข้าใจของฉันได้เล็กน้อย คำตอบของคุณดีที่สุดตอบคำถามต้นฉบับตามที่ระบุไว้ในขณะที่ Pointy และ tvanfosson ตอบคำถามที่ฉันพยายามถาม ฉันหวังว่าฉันจะเลือกได้สองคำตอบ ขอบคุณที่ 'โยนคำตอบนี้ลงในส่วนผสม' :)
Luther Baker

@ user113716 ฉันคิดว่าฟังก์ชั่นภายในไม่ได้รับประกันว่าจะเรียกก่อนeach alertคุณช่วยอธิบายหรือชี้ให้ฉันอ่านในสิ่งนี้ได้ไหม?
Maciej Jankowski

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
}

อย่างที่ฉันบอกว่ามันไม่ได้สวยที่สุด แต่มันใช้งานได้และทำงานได้ดีและฉันยังไม่พบทางออกที่ดีกว่า

ไชโย


1
ไม่มีสิ่งนี้ไม่ทำงาน มันจะยิงทุก. each และจากนั้นสิ่งสุดท้ายและทุกอย่างจะทำงานในเวลาเดียวกัน ลองวาง setTimeout ลงใน. each ของคุณแล้วคุณจะเห็นว่ามันใช้งาน flag1 === flag2 ทันที
Michael Fever

1

หากคุณยินดีที่จะทำให้เป็นสองขั้นตอนอาจใช้งานได้ มันขึ้นอยู่กับอนิเมชั่นที่จบตามลำดับ ฉันไม่คิดว่าจะเป็นปัญหา

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

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

สิ่งนี้จะใช้ได้ - แม้ว่าไม่ใช่สิ่งที่ฉันคาดหวัง อย่างไร้เดียงสาฉันกำลังมองหาบางอย่างเช่น elems.each (foo, bar) โดยที่ foo = 'ฟังก์ชั่นที่ใช้กับแต่ละองค์ประกอบ' และ bar = 'การโทรกลับหลังจากการทำซ้ำทั้งหมดเสร็จสิ้นแล้ว' ฉันจะให้คำถามบิตเวลามากขึ้นเพื่อดูว่าเราเจอมุมมืดบางส่วนของ jQuery :)
ลูเทอร์เบเกอร์

AFAIK - คุณต้องเปิดใช้งานเมื่อแอนิเมชั่น "สุดท้าย" เสร็จสิ้นและเนื่องจากมันทำงานแบบอะซิงโครนัสคุณจะต้องทำในการโทรกลับแอนิเมชั่น ฉันชอบรุ่นที่ @ Pointy ของสิ่งที่ฉันเขียนได้ดีกว่าเพราะมันไม่ได้ขึ้นอยู่กับการสั่งซื้อไม่ใช่ว่าฉันคิดว่ามันจะเป็นปัญหา
tvanfosson

0

เกี่ยวกับอะไร

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

สิ่งนี้เรียกใช้ฟังก์ชั่นของฉัน () หนึ่งครั้ง (และแนะนำให้ฉันรู้จักกับแนวคิด jQuery อื่น) แต่สิ่งที่ฉันกำลังมองหาคือการรับประกันว่า 'เมื่อ' มันจะทำงาน - ไม่ใช่แค่ว่ามันจะทำงานครั้งเดียว และอย่างที่ Patrick พูดไว้ส่วนนั้นก็ค่อนข้างง่าย สิ่งที่ฉันกำลังมองหาจริงๆคือวิธีที่จะเรียกบางสิ่งบางอย่างหลังจากตรรกะ fadeOut สุดท้ายวิ่งซึ่งฉันไม่คิดว่า. one () รับประกัน
Luther Baker

ฉันคิดว่าโซ่ทำเช่นนั้น - ตั้งแต่ส่วนแรกวิ่งก่อนส่วนสุดท้าย
Mark Schultheiss

0

คุณต้องเข้าคิวคำขอที่เหลือเพื่อให้ทำงานได้

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

ฉันพบปัญหาเดียวกันและฉันแก้ไขด้วยวิธีแก้ปัญหาเช่นรหัสต่อไปนี้:

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 () โดยใช้วัตถุที่ถูกเลื่อน


คุณไม่มีวงเล็บปิดอยู่ก่อนหน้านี้: drfs.push (internal.promise ()); อย่างน้อยฉันก็คิดถึงมันมาก่อน ..
Michael Fever

0

อาจเป็นคำตอบที่ล่าช้า แต่มีแพ็คเกจที่จะจัดการ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

ฉันกำลังใช้สิ่งนี้:

$.when(
           $.each(yourArray, function (key, value) {
                // Do Something in loop here
            })
          ).then(function () {
               // After loop ends.
          });
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.