web-dev-qa-db-ja.com

.each()が完了した後にjQuery関数を呼び出す

JQueryでは、コールバックを呼び出すまたはイベントをトリガーする.each()(またはその他のタイプの反復コールバック)の呼び出し後、完了があります。

たとえば、この「フェードと削除」を完了させたい

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

いくつかの計算を行い、$(parentSelect)の後にnew要素を挿入する前に。既存の要素がjQueryにまだ表示されていて、任意の時間(各要素に200)をスリープ/遅延すると、せいぜい脆弱なソリューションのように思える場合、私の計算は正しくありません。

イベントコールバックに必要なロジックを簡単に.bind()できますが、上記の繰り返しがcompletedになった後に.trigger()をきれいに呼び出す方法がわかりません。明らかに、複数回起動するため、反復内でトリガーを呼び出すことはできません。

$.each()の場合、データ引数の最後に何かを追加することを検討しました(反復本体で手動で検索します)が、それを強制するのは嫌なので、他の何かがあることを望んでいました反復コールバックに関してフローを制御するエレガントな方法。

175
Luther Baker

@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()反復の非同期操作startedは、もちろん独自の方法で続行されます。これが問題です。要素をフェードする呼び出しはタイマー駆動のアニメーションであり、それらは独自のペースで続行します。

したがって、上記のソリューションは、フェードされている要素の数を追跡します。 .fadeOut()を呼び出すたびに、完了コールバックが取得されます。コールバックが、関連するすべての元の要素を介してカウントされたことに気付いた場合、すべてのフェードが終了したという確信を持って、後続のアクションを実行できます。

これは4年前の回答です(2014年のこの時点で)。これを行う最新の方法は、おそらく遅延/約束メカニズムを使用することを含むでしょうが、上記は単純であり、うまく機能するはずです。

155
Pointy

おそらく遅くなりますが、このコードは機能すると思います...

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

わかりました、これは事実の少し後かもしれませんが、.promise()はまたあなたが求めているものを達成するはずです。

約束文書

私が取り組んでいるプロジェクトの例:

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

:)

156
nbsp

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

アラートが呼び出されると、each()が完了するまでアラートは実行されないため、カウントは1000000になります。

22
user113716

Jsonオブジェクトではなく、配列を扱う多くの応答を見つけました。私の解決策は、カウンターをインクリメントしながらオブジェクトを1回繰り返し、それからオブジェクトを繰り返してコードを実行するときに、2番目のカウンターをインクリメントすることでした。次に、2つのカウンターを比較して、ソリューションを取得します。私はそれが少し不格好であることを知っていますが、私はこれまでのところよりエレガントな解決策を見つけていません。これは私のコード例です:

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
}

私が言ったように、それは最もエレガントではありませんが、それは動作し、うまく動作し、私はまだより良い解決策を見つけていません。

乾杯、JP

5
JimP

あなたがそれをいくつかのステップにしたいなら、これはうまくいくかもしれません。ただし、アニメーションが順番に終了することに依存しています。私はそれが問題になるとは思わない。

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

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

どう?

$(parentSelect).nextAll().fadeOut(200, function() { 
    $(this).remove(); 
}).one(function(){
    myfunction();
}); 
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
Wim Pruiksma

それが機能するためには、リクエストの残りをキューに入れる必要があります。

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
Elyx0

私は同じ問題に出会い、次のコードのような解決策で解決しました:

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

});

このソリューションは、次の問題を解決します。Deferredオブジェクトを使用して、.each()反復で開始された非同期操作を同期します。

0