Gọi một hàm jQuery sau khi .each () đã hoàn thành


182

Trong jQuery, có thể gọi một cuộc gọi lại hoặc kích hoạt một sự kiện sau khi một lệnh gọi .each()(hoặc bất kỳ loại gọi lại lặp lại nào khác) đã hoàn thành .

Ví dụ, tôi muốn "làm mờ và xóa" này để hoàn thành

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

trước khi thực hiện một số tính toán và chèn các phần tử mới sau $(parentSelect). Tính toán của tôi không chính xác nếu các phần tử hiện có vẫn hiển thị với jQuery và ngủ / trì hoãn một lượng thời gian tùy ý (200 cho mỗi phần tử) có vẻ như là một giải pháp dễ vỡ nhất.

Tôi có thể dễ dàng .bind()logic cần thiết cho một cuộc gọi lại sự kiện nhưng tôi không chắc làm thế nào để gọi một cách sạch sẽ .trigger()sau khi việc lặp lại ở trên đã hoàn thành . Rõ ràng, tôi không thể gọi kích hoạt trong vòng lặp vì nó sẽ bắn nhiều lần.

Trong trường hợp $.each(), tôi đã cân nhắc thêm một cái gì đó vào cuối đối số dữ liệu (mà tôi tìm kiếm thủ công trong phần thân lặp) nhưng tôi ghét bị ép buộc vì vậy tôi hy vọng có một số thanh lịch khác cách để kiểm soát dòng chảy đối với các cuộc gọi lại lặp đi lặp lại.


1
Tôi có hiểu chính xác rằng đó không phải là quá nhiều ".each ()" mà bạn muốn hoàn thành, mà là tất cả các hình ảnh động được khởi chạy bởi lệnh gọi ".each ()"?
Pointy

Đó là một điểm tốt. Với câu hỏi cụ thể này, vâng, tôi chủ yếu quan tâm đến việc hoàn thành ".each ()" ... nhưng tôi nghĩ bạn đưa ra một câu hỏi khả thi khác.
Luther Baker

Câu trả lời:


161

Một thay thế cho câu trả lời của @ tv:

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

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

Lưu ý rằng .each()chính nó là đồng bộ - câu lệnh theo sau cuộc gọi .each()sẽ chỉ được thực hiện sau khi .each()cuộc gọi kết thúc. Tuy nhiên, hoạt động không đồng bộ bắt đầu trong vòng .each()lặp tất nhiên sẽ tiếp tục theo cách riêng của chúng. Đó là vấn đề ở đây: các lời kêu gọi làm mờ dần các yếu tố là hình ảnh động theo thời gian và chúng tiếp tục với tốc độ của riêng chúng.

Do đó, giải pháp ở trên, theo dõi xem có bao nhiêu yếu tố đang bị mờ dần. Mỗi cuộc gọi để .fadeOut()có được một cuộc gọi lại hoàn thành. Khi thông báo gọi lại rằng nó được tính thông qua tất cả các yếu tố ban đầu có liên quan, một số hành động tiếp theo có thể được thực hiện với sự tự tin rằng tất cả các mờ dần đã kết thúc.

Đây là một câu trả lời bốn năm tuổi (tại thời điểm này trong năm 2014). Một cách hiện đại để làm điều này có thể sẽ liên quan đến việc sử dụng cơ chế Trì hoãn / Hứa hẹn, mặc dù cách trên là đơn giản và sẽ hoạt động tốt.


4
Tôi thích thay đổi này, mặc dù tôi sẽ so sánh bằng 0 thay vì phủ định logic (--count == 0) kể từ khi bạn đếm ngược. Đối với tôi nó làm cho ý định rõ ràng hơn, mặc dù nó có tác dụng tương tự.
tvanfosson

Khi nó bật ra, nhận xét ban đầu của bạn về câu hỏi là tại chỗ. Tôi đã hỏi về .each () và nghĩ rằng đó là những gì tôi muốn nhưng như bạn và tvanfosson và bây giờ patrick đã chỉ ra - đó là sự mờ nhạt cuối cùng mà tôi thực sự quan tâm. Tôi nghĩ tất cả chúng ta đều đồng ý rằng ví dụ của bạn của các chỉ số) có lẽ là an toàn nhất.
Luther Baker

@ A.DIMO Ý bạn là gì, "quá phức tạp"? Phần nào phức tạp?
Mũi nhọn

169

Có lẽ đã muộn nhưng tôi nghĩ mã này hoạt động ...

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

156

Ok, điều này có thể là một chút sau khi thực tế, nhưng .promise () cũng sẽ đạt được những gì bạn đang theo đuổi.

Tài liệu hứa hẹn

Một ví dụ từ một dự án tôi đang làm việc:

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

:)


7
.promise () không khả dụng trên .each ()
Michael Fever

25

JavaScript chạy đồng bộ, do đó, bất cứ điều gì bạn đặt sau each()sẽ không chạy cho đến khieach() hoàn tất.

Hãy xem xét các bài kiểm tra sau:

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

Khi cảnh báo được gọi, số đếm sẽ bằng 1000000 vì cảnh báo sẽ không chạy cho đến khi each()hoàn tất.


8
Vấn đề trong ví dụ đưa ra là fadeOut được đưa vào hàng đợi hoạt hình và không thực thi đồng bộ. Nếu bạn sử dụng eachvà lên lịch cho từng hoạt hình một cách riêng biệt, bạn vẫn phải kích hoạt sự kiện sau khi hoạt hình chứ không phải mỗi kết thúc.
tvanfosson

3
@tvanfosson - Vâng, tôi biết. Theo những gì Luther muốn thực hiện, giải pháp của bạn (và của Pointy) có vẻ như là phương pháp đúng đắn. Nhưng từ những bình luận của Luther như ... Naively, tôi đang tìm kiếm thứ gì đó như elems.each (foo, bar) trong đó hàm foo = 'được áp dụng cho từng thành phần' và bar = 'gọi lại sau khi tất cả các lần lặp lại đã hoàn thành'. ... Anh ta có vẻ khăng khăng rằng đó là một each()vấn đề. Đó là lý do tại sao tôi ném câu trả lời này vào hỗn hợp.
dùng113716

Bạn đúng Patrick. Tôi đã (mis) hiểu rằng .each () đang lên lịch hoặc xếp hàng các cuộc gọi lại của tôi. Tôi nghĩ rằng việc gọi fadeOut đã gián tiếp dẫn tôi đến kết luận đó. tvanfosson và Pointy đều đã cung cấp câu trả lời cho vấn đề fadeOut (đó là những gì tôi thực sự sau đó) nhưng bài viết của bạn thực sự điều chỉnh sự hiểu biết của tôi một chút. Câu trả lời của bạn thực sự trả lời tốt nhất câu hỏi ban đầu như đã nêu trong khi Pointy và tvanfosson trả lời câu hỏi tôi đang cố gắng hỏi. Tôi ước tôi có thể chọn hai câu trả lời. Cảm ơn vì 'ném câu trả lời này vào hỗn hợp' :)
Luther Baker

@ user113716 Tôi nghĩ rằng các chức năng bên trong eachkhông được đảm bảo để được gọi trước đó alert. bạn có thể giải thích hoặc chỉ cho tôi đọc về điều này?
Maciej Jankowski

5

Tôi tìm thấy rất nhiều phản hồi xử lý các mảng nhưng không phải với một đối tượng json. Giải pháp của tôi chỉ đơn giản là lặp qua đối tượng một lần trong khi tăng bộ đếm và sau đó khi lặp qua đối tượng để thực hiện mã của bạn, bạn có thể tăng bộ đếm thứ hai. Sau đó, bạn chỉ cần so sánh hai quầy với nhau và có được giải pháp của bạn. Tôi biết đó là một chút lộn xộn nhưng tôi đã không tìm thấy một giải pháp thanh lịch hơn cho đến nay. Đây là mã ví dụ của tôi:

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
}

Như tôi đã nói, nó không phải là thanh lịch nhất, nhưng nó hoạt động và nó hoạt động tốt và tôi chưa tìm thấy một giải pháp tốt hơn.

Chúc mừng, JP


1
Không, cái này không hoạt động. Nó bắn tất cả .each và sau đó là điều cuối cùng và mọi thứ chạy cùng một lúc. Hãy thử đặt setTimeout trên .each của bạn và bạn sẽ thấy nó chỉ chạy cờ1 === flag2 ngay lập tức
Michael Fever

1

Nếu bạn sẵn sàng thực hiện một vài bước, điều này có thể hoạt động. Tuy nhiên, nó phụ thuộc vào hoạt hình hoàn thiện theo thứ tự. Tôi không nghĩ đó là một vấn đề.

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

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

Điều này sẽ làm việc - mặc dù, không phải những gì tôi hy vọng. Ngây thơ, tôi đang tìm kiếm một cái gì đó như elems.each (foo, bar) trong đó hàm foo = 'được áp dụng cho từng phần tử' và bar = 'gọi lại sau khi tất cả các lần lặp lại đã hoàn thành'. Tôi sẽ đưa ra câu hỏi thêm một chút thời gian để xem liệu chúng ta có bắt gặp một số góc tối của jQuery không :)
Luther Baker

AFAIK - bạn phải kích hoạt nó khi hoạt ảnh "cuối cùng" hoàn thành và vì chúng chạy không đồng bộ, bạn phải thực hiện điều đó trong cuộc gọi lại hoạt hình. Tôi thích phiên bản @ Pointy của những gì tôi đã viết tốt hơn vì nó không phụ thuộc vào thứ tự, không phải tôi nghĩ đó sẽ là một vấn đề.
tvanfosson

0

Thế còn

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

Điều này chắc chắn sẽ gọi hàm của tôi () một lần (và giới thiệu cho tôi một khái niệm jQuery khác) nhưng điều tôi đang tìm kiếm là sự đảm bảo về 'khi nào' nó sẽ chạy - không chỉ đơn thuần là nó sẽ chạy một lần. Và, như Patrick đề cập, phần đó hóa ra khá dễ dàng. Điều tôi thực sự đang tìm kiếm là một cách để gọi một cái gì đó sau khi logic mờ dần cuối cùng chạy, tôi không nghĩ .one () đảm bảo.
Luther Baker

Tôi nghĩ rằng chuỗi làm điều đó - vì phần đầu tiên chạy trước phần cuối cùng.
Mark Schultheiss

0

Bạn phải xếp hàng phần còn lại của yêu cầu của bạn để nó hoạt động.

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

Tôi gặp cùng một vấn đề và tôi đã giải quyết bằng một giải pháp như đoạn mã sau:

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

});

Giải pháp giải quyết vấn đề sau: để đồng bộ hóa các hoạt động không đồng bộ được bắt đầu trong phép lặp .each (), sử dụng đối tượng Trì hoãn.


Bạn đang thiếu một khung đóng) trước: drfs.push (Internal.promise ()); ít nhất là tôi nghĩ nó trước đây ..
Michael Fever

0

Có thể phản hồi muộn nhưng có một gói để xử lý https://github.com/ACFBentveld/Await này

 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

Tôi đang sử dụng một cái gì đó như thế này:

$.when(
           $.each(yourArray, function (key, value) {
                // Do Something in loop here
            })
          ).then(function () {
               // After loop ends.
          });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.